DP(1)——线性DP

贪心只能过样例,DP一般看规律……
Dynamic Programming,teach you how to program your life.
DP,全称动态规划(dynamic programming)是运筹学的一个分支,是求解多阶
段决策过程最优化的数学方法。多阶段决策过程,是指这样的一类特殊的活动过程,问题可以按时间顺序分解成若干相互联系的阶段,在每一个阶段都要做出决策,全部
过程的决策是一个决策序列。DP最显著的特点是最优化,无后效性,这为我们解决问题提供了良好的基础。
最长上升子序列
给你n个数,求最长上升子序列的长度。
在这里,我们可以设一个f数组,f_i表示前i位最长上升子序列的长度。对于第i位,我们需要判断这一位是否比前面i-1位大,如果比前面的大,那么在满足条件的i-1位中选f的值最大的累加1,即可求出。

#include<bits/stdc++.h>
using namespace std;
int n,a[10010],f[10010];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
     cin>>a[i];
    f[1]=1;
    for(int i=2;i<=n;i++)
     for(int j=1;j<i;j++)
     {
        if(a[i]>a[j])
         f[i]=max(f[i],f[j]+1); 
     }
    cout<<f[n]<<endl;
    return 0;
}

最长公共子串
给定字符串a和b,求a和b的最长公共子串的长度。
这也是一道非常明显的线性DP,我们依旧用f来存储最优解,对于第i位,我们需要判断字符串a的第i位和字符串b的第j位是否相同,如果相同那么就在上一次的基础上+1,不同,那么就在f_i-1,j和f_i,j-1中去最大值,不累加。

#include<bits/stdc++.h>
using namespace std;
string a,b;
int f[110][110];
int main()
{
    cin>>a>>b;
    if(a[0]==b[0])
     f[0][0]=1;
    else
     f[0][0]=0;
    for(int i=0;i<=a.size()-1;i++)
     for(int j=0;j<=b.size()-1;j++)
     {
        if(a[i]==b[j])
         f[i][j]=f[i-1][j-1]+1;
        else
         f[i][j]=max(f[i-1][j],f[i][j-1]);
     }
     cout<<f[a.size()-1][b.size()-1]<<endl;
     return 0;
}

另外,对于多个字符串,我们选择加维,有几个就加几维。
合唱队形
N位同学站成一排,音乐老师要挑出K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T 1 ,T 2 ,…,T K ,则他们的身高满足T 1 < T 2 < ··· < T i ,T i > T i+1 > ··· > T K (1 ≤ i ≤ K)。
这道题和最长上升子序列很像,不同的是它到达最高点后会变成下降,那么反过来看,从后到前又可以看成一个最长上升子序列。正反做两次,再取最高点,求出的就是保留的人数,相减就可以得出答案。

#include<bits/stdc++.h>
using namespace std;
int n,a[10001],f[10001],g[10001],ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
     cin>>a[i];
    for(int i=1;i<=n;i++)//正着求最长上升子序列
     {
      f[i]=1;
     for(int j=1;j<=i;j++)
      if(a[i]>a[j])
       f[i]=max(f[i],f[j]+1);
      }
    for(int i=n;i>=1;i--)//倒着求最长上升子序列
     {
      g[i]=1;
     for(int j=n;j>=i;j--)
      if(a[i]>a[j])
       g[i]=max(g[i],g[j]+1);
     }
    for(int i=1;i<=n;i++)//寻找中间点
     ans=max(f[i]+g[i]-1,ans);
    cout<<n-ans<<endl;
    return 0;
}

轮船问题
一条河的两岸有n对城市,希望对应两条之间有一条航线,但不能交叉,问能开通多少条航线。
首先,我们对左岸的城市进行排序,然后对右岸进行求最长上升子序列,最长的最长上升子序列就是开通的航线。(不要问我为什么,说不清)

#include<bits/stdc++.h>
using namespace std;
struct road
{
    int r,l;
}a[5110]={};
bool cmp(road c,road d)
{
    return c.l<d.l;
}
int main()
{
    int x,y,n,maxx=0;
    cin>>x>>y>>n;
    int f[5110]={};
    for(int i=1;i<=n;i++)
     cin>>a[i].l>>a[i].r;
    sort(a+1,a+n+1,cmp);
    f[1]=1;
    for(int i=2;i<=n;i++)
    {
     for(int j=1;j<i;j++)
      if(a[i].r>a[j].r&&f[j]>f[i]) f[i]=f[j];
     f[i]++;
    }
    for(int i=1;i<=n;i++)
    if(f[i]>maxx)
    maxx=f[i];
    cout<<maxx<<endl;
    return 0;
} 

线性DP如上文所述,背包问题再说吧……

猜你喜欢

转载自blog.csdn.net/smileyx2005/article/details/81055965
今日推荐