回顾一些较简单的dp题

Posted forward777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回顾一些较简单的dp题相关的知识,希望对你有一定的参考价值。

1.导弹拦截  (+贪心)

  两问:一个导弹拦截系统最多能拦多少导弹 要拦截所有导弹至少需要多少拦截系统

  第一问感觉是一个比较巧妙的方法:

    维护一个单调递减的序列 length[] 记录的是拦截导弹的高度

    当下一个导弹小于 length[] 最后一个数(最小的数)则直接把它加在序列后即可

    若大于 则找到序列中比它大的最小的数(二分)然后替换 可以保证最优

  第二问 就是贪心啊

    当现有的导弹系统的拦截高度都小于当前导弹的高度 则开一个新的系统

    否则找到拦截高度比导弹高度高的最小的系统来拦截

    这里记录系统拦截高度的数组一定是单调递增的无需排序

技术分享图片
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int x;
bool f;
int height[100010],length[100010],minh[100010];
int main()
{
    int y=1;
    while(scanf("%d",&height[y])==1) y++;y--;
    length[1]=height[1];
    int top=1;
    for(int i=2;i<=y;i++)
    {
        if(height[i]<=length[top]) length[++top]=height[i];
        else
        {
            int left=1,right=top,ans;
            while(left<=right)
            {
                int mid=(left+right)/2;
                if(length[mid]<height[i])
                {
                    ans=mid;
                    right=mid-1;
                }
                else left=mid+1;
            }
            length[ans]=height[i];
        }
    }  
    cout<<top<<endl;
    int maxa=0;
    for(int i=1;i<=y;i++)
    if(maxa<length[i]) maxa=length[i];
    int num=1;minh[1]=height[1];
    for(int i=2;i<=y;i++)
    {
        f=0;
        for(int j=1;j<=num;j++)
         if(height[i]<=minh[j]) {minh[j]=height[i];f=1;break;}
        if(f==0) minh[++num]=height[i];
    }
    cout<<num;
    return 0;
}
View Code

 

2.石子合并 (+断环为链法)

  区间dp一般套路:枚举起点 枚举区间长度(或终点) 枚举断点 转移

  注意枚举的顺序要保证由已知推未知

  很多时候遇到环都可能要断环为链(图论也是这样)

技术分享图片
 1 #include<iostream>
 2 using namespace std;
 3 int a[210];
 4 int s[210];
 5 int ans1[210][210];
 6 int ans2[210][210];
 7 int main()
 8 {
 9     int n;
10     cin>>n;
11     for(int i=1;i<=n;i++)
12     {
13         cin>>a[i];
14         s[i]=s[i-1]+a[i];
15         a[i+n]=a[i];
16     }
17     for(int i=n+1;i<=n*2;i++)
18     s[i]=s[i-1]+a[i];
19     for(int i=1;i<=2*n;i++)
20     for(int j=1;j<=2*n;j++)
21     if(j!=i) ans2[i][j]=2100000;
22     for(int h=1;h<=n;h++)
23     {
24         for(int i=h+n-1;i>=h;i--)
25         {
26             for(int j=i;j<=h+n-1;j++)
27             {
28                 for(int k=i+1;k<=j;k++)
29                 {
30                     ans1[i][j]=max(ans1[i][j],ans1[i][k-1]+ans1[k][j]+s[j]-s[i-1]);
31                     ans2[i][j]=min(ans2[i][j],ans2[i][k-1]+ans2[k][j]+s[j]-s[i-1]);
32                 }
33             }
34         }
35     }
36     int maxn,minn=2100000;
37     for(int i=1;i<=n;i++)
38     {
39         maxn=max(maxn,ans1[i][i+n-1]);
40         minn=min(minn,ans2[i][i+n-1]);
41     }
42     cout<<minn<<endl<<maxn<<endl;
43     return 0;
44 } 
View Code

 

3.相似基因 

  现在看起来好像很显然的样子

  f[i][j] 表示第一个字符串匹配到第i位 第二个字符串匹配到第j位最大的相似度

  三种情况转移 分别是在第一个,第二个字符串加空碱基 不加空碱基

  注意初始化

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int len1,len2,sum;
 5 char s1[110],s2[110];
 6 int a1[110],a2[110];
 7 char ch;
 8 int f[110][110];
 9 int s[6][6]={{0,0,0,0,0,0},
10              {0,5,-1,-2,-1,-3},
11              {0,-1,5,-3,-2,-4},
12              {0,-2,-3,5,-2,-2},
13              {0,-1,-2,-2,5,-1},
14              {0,-3,-4,-2,-1,0}};
15 int change(char x)
16 {
17     if(x==A) return 1;
18     else if(x==C) return 2;
19     else if(x==G) return 3;
20     else if(x==T) return 4;
21 }
22 int main()
23 {
24     scanf("%d",&len1);ch=getchar();
25     for(int i=1;i<=len1;i++) scanf("%c",&s1[i]);        
26     scanf("%d",&len2);ch=getchar();
27     for(int i=1;i<=len2;i++) scanf("%c",&s2[i]);
28     for(int i=1;i<=len1;i++) a1[i]=change(s1[i]);
29     for(int i=1;i<=len2;i++) a2[i]=change(s2[i]);
30     for(int i=1;i<=len1;i++)
31         for(int j=1;j<=len2;j++) f[i][j]=-21000000;
32     for(int i=1;i<=len1;i++) f[i][0]=f[i-1][0]+s[a1[i]][5];
33     for(int i=1;i<=len2;i++) f[0][i]=f[0][i-1]+s[a2[i]][5];
34     for(int i=1;i<=len1;i++)
35     {
36         for(int j=1;j<=len2;j++)
37         {
38             f[i][j]=max(f[i][j],f[i-1][j]+s[a1[i]][5]);
39             f[i][j]=max(f[i][j],f[i][j-1]+s[a2[j]][5]);
40             f[i][j]=max(f[i][j],f[i-1][j-1]+s[a1[i]][a2[j]]);
41         }
42     }
43     printf("%d",f[len1][len2]);
44     return 0;
45 }
View Code

 

4.传纸条(优化维度)

  四维dp 优化成三维

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int read()
 5 {
 6     int ans=0;char c;
 7     c=getchar();
 8     while(c<0||c>9) c=getchar();
 9     while(c>=0&&c<=9) {ans=ans*10+c-0;c=getchar();}
10     return ans;
11 }
12 int f[1010][510][510];
13 int a[510][510];
14 int main()
15 {
16     int m=read(),n=read();
17     for(int i=1;i<=m;i++)
18     for(int j=1;j<=n;j++)
19     a[i][j]=read();
20     for(int k=1;k<=n+m;k++)
21     {
22         for(int i=1;i<=n;i++)
23         {
24             for(int j=1;j<=n;j++)
25             {
26                 if(k-i+1<1||k-j+1<1) continue;
27                 f[k][i][j]=max(max(max(f[k-1][i][j],f[k-1][i-1][j]),f[k-1][i][j-1]),f[k-1][i-1][j-1]);
28                 f[k][i][j]+=a[k-i][i]+a[k-j][j];
29                 if(i==j) f[k][i][j]-=a[k-i][i];
30             }
31         }
32     }
33     cout<<f[m+n][n][n];
34     return 0;
35 } 
View Code

 

5.花店橱窗布置

  这一题要输出最优方案 所以f[i][j]表示的是前i束花放在前j个瓶子里 且第i束花放在第j个瓶子里的最大美学值

技术分享图片
 1 #include<iostream>
 2 using namespace std;
 3 int a[110][110];  //a[i][j] 第i束花放在第j个花瓶中的美学值
 4 int b[110][110];  //b[i][j] 前i束花放在前j个花瓶中的最大美学值 
 5 int c[110][110],d[110];
 6 int main()
 7 {
 8     int f,v;
 9     cin>>f>>v;
10     for(int i=1;i<=f;i++)
11     for(int j=1;j<=v;j++)
12     cin>>a[i][j];
13     //for(int i=1;i<=v-f+1;i++) b[1][i]=a[1][i];
14     for(int i=1;i<=f;i++)
15     for(int j=1;j<=v;j++)
16     b[i][j]=-2100000000; 
17     /*如果b数组中初始值都为0
18     那么当第一束花放在前几个花瓶中美学值为负数时就会出错;
19     或:直接初始化第一束花放在前几个花瓶中的美学值(前面被注释掉的)
20     但注意此时后面一个循环的i从2开始*/ 
21        
22     for(int i=1;i<=f;i++)
23     for(int j=i;j<=v-f+i;j++) //j<=v-f+i!!!
24     for(int k=i-1;k<=j-1;k++)
25     {
26         if(b[i][j]<b[i-1][k]+a[i][j])
27         {
28             b[i][j]=b[i-1][k]+a[i][j];
29             c[i][j]=k;
30         }
31     }
32     int maxn=-2100000000,k;
33     for(int i=f;i<=v;i++)
34     {
35         if(b[f][i]>maxn)
36         {
37             maxn=b[f][i];
38             k=i;
39         }
40     }
41     cout<<maxn<<endl;
42     for(int i=1;i<=f;i++)
43     {
44         d[i]=k;
45         k=c[f-i+1][k];
46     }
47     for(int j=f;j>=1;j--) cout<<d[j]<<" ";
48     return 0; 
49 }
View Code

 

6.编辑距离

  和3类似

技术分享图片
 1 #include<iostream>
 2 #include<cstring> 
 3 #include<cstdio>
 4 using namespace std;
 5 char a[2010],b[2010];
 6 int f[2010][2010];
 7 int main()
 8 {
 9     int m,n,i,j;
10     scanf("%s%s",a,b);
11     m=strlen(a);n=strlen(b);
12     for(int i=m+1;i>=1;i--) a[i]=a[i-1];
13     for(int j=n+1;j>=1;j--) b[j]=b[j-1]; 
14     for(i=1;i<=m;i++) f[i][0]=i;
15     for(j=1;j<=n;j++) f[0][j]=j;
16     for(i=1;i<=m;i++)
17     {
18         for(j=1;j<=n;j++)
19         {
20             if(a[i]==b[j]) f[i][j]=f[i-1][j-1];
21             else
22             {
23                 f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
24             }
25         }
26     }
27     cout<<f[m][n];
28     return 0;
29 }
View Code

 

7.乘积最大

  f[i][j] 表示到第i位数已经加了j个乘号的最大乘积

  加高精有点麻烦啊

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<string>
 5 #define rg register
 6 #define M 110
 7 using namespace std;
 8 int read()
 9 {
10   int x=0,y=1;char c;
11   c=getchar();
12   while(c<0||c>9) {if(c==-) y=-1;c=getchar();}
13   while(c>=0&&c<=9) {x=(x<<1)+(x<<3)+c-0;c=getchar();}
14   return x*y;
15 }
16 string ss;
17 int n1,k1;
18 int a[M];
19 struct node
20 {
21   int s[M];
22   int len;
23 }f[10][M];
24 node calc(node x,int l,int r)
25 {
26   node x1,y1;y1.len=r-l+1;
27   int len1=x.len,len2=y1.len,len=len1+len2-1;
28   memset(x1.s,0,sizeof(x1.s));
29   memset(y1.s,0,sizeof(y1.s));
30   for(rg int i=r;i>=l;i--) y1.s[r-i+1]=a[i];
31   for(rg int i=1;i<=len1;i++)
32     for(rg int j=1;j<=len2;j++)
33       x1.s[i+j-1]+=x.s[i]*y1.s[j];
34   for(rg int i=1;i<=len;i++)
35     {
36       x1.s[i+1]+=x1.s[i]/10;
37       x1.s[i]%=10;
38     }
39   if(x1.s[len+1]) len++;
40   x1.len=len;
41   return x1;
42 }
43 node cmp(node x,node y)
44 {
45   int len1=x.len,len2=y.len;
46   if(len1<len2) return y;
47   if(len1>len2) return x;
48   for(rg int i=len1;i>=1;i--)
49     {
50       if(x.s[i]>y.s[i]) return x;
51       if(x.s[i]<y.s[i]) return y;
52     }
53   return y;
54 }
55 int main()
56 {
57   n1=read();k1=read();
58   cin>>ss;
59   for(int i=1;i<=n1;i++) a[i]=ss[i-1]-0;
60   for(int i=1;i<=n1;i++)
61     for(int j=i;j>=1;j--)
62       f[0][i].s[++f[0][i].len]=a[j];
63   for(int i=2;i<=n1;i++) //前i个数
64     {
65       int maxn=min(i-1,k1);
66       for(int k=1;k<=maxn;k++)//k个乘号
67       for(int j=k;j<i;j++)//第k个乘号放哪
68       f[k][i]=cmp(f[k][i],calc(f[k-1][j],j+1,i));
69     }
70    for(int i=f[k1][n1].len;i>=1;i--)
71     printf("%d",f[k1][n1].s[i]);
72       return 0;
73 }
View Code

 




以上是关于回顾一些较简单的dp题的主要内容,如果未能解决你的问题,请参考以下文章

2017级算法模拟上机准备篇

799. 香槟塔 : 简单线性 DP 运用题

一些知识

从集合的角度思考DP问题

斜率优化系列——训练记录

原创这道Java基础题真的有坑!我也没想到还有续集。