回顾一些较简单的dp题
Posted forward777
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回顾一些较简单的dp题相关的知识,希望对你有一定的参考价值。
两问:一个导弹拦截系统最多能拦多少导弹 要拦截所有导弹至少需要多少拦截系统
第一问感觉是一个比较巧妙的方法:
维护一个单调递减的序列 length[] 记录的是拦截导弹的高度
当下一个导弹小于 length[] 最后一个数(最小的数)则直接把它加在序列后即可
若大于 则找到序列中比它大的最小的数(二分)然后替换 可以保证最优
第二问 就是贪心啊
当现有的导弹系统的拦截高度都小于当前导弹的高度 则开一个新的系统
否则找到拦截高度比导弹高度高的最小的系统来拦截
这里记录系统拦截高度的数组一定是单调递增的无需排序
View Code#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; }
区间dp一般套路:枚举起点 枚举区间长度(或终点) 枚举断点 转移
注意枚举的顺序要保证由已知推未知
很多时候遇到环都可能要断环为链(图论也是这样)
View Code1 #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 }
现在看起来好像很显然的样子
f[i][j] 表示第一个字符串匹配到第i位 第二个字符串匹配到第j位最大的相似度
三种情况转移 分别是在第一个,第二个字符串加空碱基 不加空碱基
注意初始化
View Code1 #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 }
四维dp 优化成三维
View Code1 #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 }
这一题要输出最优方案 所以f[i][j]表示的是前i束花放在前j个瓶子里 且第i束花放在第j个瓶子里的最大美学值
View Code1 #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 }
和3类似
View Code1 #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 }
f[i][j] 表示到第i位数已经加了j个乘号的最大乘积
加高精有点麻烦啊
View Code1 #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 }
以上是关于回顾一些较简单的dp题的主要内容,如果未能解决你的问题,请参考以下文章