复习/学习DP的长记
来源:互联网 发布:淘宝新店怎样引流 编辑:程序博客网 时间:2024/06/11 14:21
01背包问题
采药
二维
#include<cstdio>#include<iostream> #include<cstring>using namespace std;int t,m;const int maxn=1005;int v[maxn],tm[maxn],dp[maxn][maxn];int main(){ memset(v,0,sizeof(v)); memset(tm,0,sizeof(tm)); memset(dp,0,sizeof(dp)); scanf("%d%d",&t,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&tm[i],&v[i]); } for(int i=1;i<=m;i++) for(int j=0;j<=t;j++) { if(j-tm[i]>=0) dp[i][j]=max(dp[i-1][j-tm[i]]+v[i],dp[i-1][j]); else dp[i][j]=dp[i-1][j]; } printf("%d\n",dp[m][t]); return 0; }//若题目要求:恰好装满容积为V的背包的最大价值:初始化dp[0][i](i!=0)=-INF//保证所有的状态都是从起点dp[0][0]转移得到的 每次装物品时,必定从已经装了体积为j-ci的物品的背包转移过来。
一维
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=1005;int t,m;int v[maxn],tm[maxn],dp[maxn];int main(){ scanf("%d%d",&t,&m); for(int i=1;i<=m;i++) scanf("%d%d",&tm[i],&v[i]); for(int i=1;i<=m;i++) for(int j=t;j>=tm[i];j--) { dp[j]=max(dp[j],dp[j-tm[i]]+v[i]); } printf("%d\n",dp[t]); return 0;}
金明的预算方案
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;int N,m,num;int sfv[300][3],sfw[300][3],dp[40000+5];int main(){ scanf("%d%d",&N,&m); for(int i=1;i<=m;i++) { int v,w; scanf("%d%d%d",&v,&w,&num); if(!num) { sfv[i][0]=v; sfw[i][0]=w; } else { if(!sfv[num][1]) { sfv[num][1]=v; sfw[num][1]=w; } else { sfv[num][2]=v; sfw[num][2]=w; } } } for(int i=1;i<=m;i++) for(int j=N;j>=sfv[i][0];j--) { dp[j]=max(dp[j-sfv[i][0]]+sfv[i][0]*sfw[i][0],dp[j]); if(j>=sfv[i][0]+sfv[i][1]) dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]] +sfv[i][0]*sfw[i][0] +sfv[i][1]*sfw[i][1] ,dp[j]); if(j>=sfv[i][0]+sfv[i][2]) dp[j]=max(dp[j-sfv[i][0]-sfv[i][2]] +sfv[i][0]*sfw[i][0] +sfv[i][2]*sfw[i][2] ,dp[j]); if(j>=sfv[i][0]+sfv[i][1]+sfv[i][2]) dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]-sfv[i][2]] +sfv[i][0]*sfw[i][0] +sfv[i][1]*sfw[i][1] +sfv[i][2]*sfw[i][2] ,dp[j]); } printf("%d\n",dp[N]);}
最长上升子序列
拦截导弹
O(n^2)
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=30000+5;int n;int a[maxn],dp[maxn],dp1[maxn];int main(){ int n=0; while(scanf("%d",&a[++n])>=1);n--; for(int i=1;i<=n;i++) dp[i]=dp1[i]=1; int ans=0,ans1=0; for(int i=1;i<=n;i++) for(int j=1;j<i;j++) { if(a[i]<=a[j]) dp[i]=max(dp[i],dp[j]+1); ans=max(ans,dp[i]); if(a[i]>a[j]) dp1[i]=max(dp1[i],dp1[j]+1); ans1=max(ans1,dp1[i]); } printf("%d\n%d\n",ans,ans1);}
合唱队形
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=30000+5;int n;int a[maxn],dp[maxn],dp1[maxn];int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) dp[i]=dp1[i]=1; int ans1=0; for(int i=1;i<=n;i++) for(int j=1;j<i;j++) { if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1); } for(int i=n;i>=1;i--) for(int j=i+1;j<=n;j++) { if(a[i]>a[j]) dp1[i]=max(dp1[i],dp1[j]+1); } for(int k=1;k<=n;k++) { ans1=max(dp[k]+dp1[k]-1,ans1); } printf("%d\n",n-ans1);}
方格取数问题
方格取数
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=11;int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];int n;int main(){ scanf("%d",&n); int x,y,v; memset(mp,0,sizeof(mp)); memset(dp,0,sizeof(dp)); while(scanf("%d%d%d",&x,&y,&v)>=3) { if(x==0&&y==0&&v==0) break; else { mp[x][y]=v; } } int sum; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { sum=i+j; for(int i1=1,j1=sum-i1;i1<=n&&i1+1<=sum;i1++,j1=sum-i1) { dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1], dp[i][j-1][i1-1][j1]), max(dp[i-1][j][i1][j1-1], dp[i][j-1][i1][j1-1])); if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j]; } } cout<<dp[n][n][n][n]<<'\n'; return 0;}
传纸条
蒟蒻的丑陋代码:
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=55;int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];int m,n;int main(){ scanf("%d%d",&m,&n); memset(mp,0,sizeof(mp)); memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) scanf("%d",&mp[i][j]); int sum; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { sum=i+j; int i1=1; for(int j1=sum-i1;i1+1<=sum;i1++,j1=sum-i1) { if(i1 > m) continue;//************ if(j1 > n) continue;//************ dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1], dp[i][j-1][i1-1][j1]), max(dp[i-1][j][i1][j1-1], dp[i][j-1][i1][j1-1])); if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j]; } } cout<<dp[m][n][m][n]<<'\n'; return 0;}
wzhd大佬的优化版:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int dp[55][55][55][55],map[55][55];int MAX(int a,int b,int c,int d){ return max(a,max(b,max(c,d)));}int main(){ int m,n; scanf("%d%d",&m,&n); for(int i = 1;i <= m;i ++) for(int j = 1;j <= n;j ++) scanf("%d",&map[i][j]); for(int i = 1;i <= m;i ++) for(int j = 1;j < n;j ++) for(int k = 1;k <= m;k ++) for(int l = j + 1;l <= n;l ++) dp[i][j][k][l] = MAX(dp[i - 1][j][k - 1][l],dp[i - 1][j][k][l - 1],dp[i][j - 1][k][l - 1],dp[i][j - 1][k - 1][l]) + map[i][j] + map[k][l]; printf("%d",dp[m][n - 1][m - 1][n]);}
最长公共子序列问题
最长公共子序列
#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;const int maxn=3000+5;long a[maxn],b[maxn],dp[maxn][maxn];int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(a[i]==b[j]) dp[i][j]=max(dp[i-1][j-1]+1, max(dp[i-1][j],dp[i][j-1])); else dp[i][j]=max(dp[i-1][j-1], max(dp[i-1][j],dp[i][j-1])); } printf("%ld\n",dp[n][n]); return 0;}
买帽子
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;int tot=0,n,dp[505][505];string s,ss;char s1[505];struct Hat{ string a; int l;}hat[505];bool cmp(Hat A,Hat B){ if(A.l==B.l) return A.a<B.a; else return A.l>B.l;}int main(){ scanf("%d",&n); while(n--) { s=" ";s1[0]=' '; cin>>ss;s+=ss; int len=s.length(); for(int j=1;j<s.length();j++) s1[j]=s[len-j];//exchange for(int i=1;i<len;i++) dp[i][len-i]=1; for(int i=1;i<len;i++) for(int j=1;j<len;j++) { if(s[i]==s1[j]) { dp[i][j]=max(dp[i-1][j-1]+1, max(dp[i-1][j],dp[i][j-1])); } else dp[i][j]=max(dp[i-1][j-1], max(dp[i-1][j],dp[i][j-1])); } hat[++tot]=(Hat){s,dp[len-1][len-1]}; } sort(hat+1,hat+1+tot,cmp); for(int i=1;i<=tot;i++) { for(int j=1;j<hat[i].a.length();j++) cout<<hat[i].a[j]; printf("\n"); } return 0;}
编辑距离问题
#include<cstdio>#include<iostream>#include<cstring>using namespace std;string aa,bb;int dp[4005][4005];int main(){ cin>>aa>>bb; string a=" ",b=" "; a+=aa,b+=bb; for(int i=1;i<a.length();i++) dp[i][0]=i;//delete for(int j=1;j<b.length();j++) dp[0][j]=j;//add for(int i=1;i<a.length();i++) for(int j=1;j<b.length();j++) { if(a[i]!=b[j]) { dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i][j-1]+1,dp[i-1][j]+1)); } else dp[i][j]=dp[i-1][j-1]; } printf("%d",dp[a.length()-1][b.length()-1]);}
区间DP
石子归并
#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;const int maxn=105;int a[maxn],dp[maxn][maxn],qzh[maxn];int main() { memset(dp,0x3f3f3f3f,sizeof(dp)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); dp[i][i]=0;//长度为0的区间 qzh[i]=qzh[i-1]+a[i]; } for(int i=n;i>=1;i--) for(int j=i+1;j<=n;j++) for(int k=i;k<j;k++)//注意不是k=i+1:保证长度为1的区间和为前缀和 { dp[i][j]=min(dp[i][k]+dp[k+1][j]+qzh[j]-qzh[i-1],dp[i][j]); } printf("%d",dp[1][n]); return 0;}
能量项链
//考试的时候做出来的,开心QwQ#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<queue>using namespace std;int n;int a[400+5];int power[400+5][400+5];int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i+n]=a[i]; } a[2*n+1]=a[1]; for(int i=2*n;i>=1;i--) for(int j=i+1;j<=2*n;j++) for(int k=i;k<j;k++) { power[i][j]=max(power[i][j],power[i][k]+power[k+1][j]+a[i]*a[k+1]*a[j+1]); } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,power[i][i+n-1]); printf("%d\n",ans); return 0;}
乘法游戏
#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;int a[100+5];int dp[105][105];int main(){ memset(dp,0x3f3f3f3f,sizeof(dp)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); dp[i-1][i]=0; } for(int i=n-2;i>=1;i--) for(int j=i+2;j<=n;j++) for(int k=i+1;k<j;k++) { dp[i][j]=min(dp[i][k]+dp[k][j]+a[i]*a[k]*a[j],dp[i][j]); } printf("%d\n",dp[1][n]); return 0;}
乘积最大
蒟蒻的5 for
#include<cstdio>#include<iostream>#include<cstring>#include<cmath>using namespace std;int n,k,dp[100][100][10],b[100];char a[100];int main(){ scanf("%d%d",&n,&k);getchar(); for(int i=0;i<=n;i++) { scanf("%c",&a[i]); if(i>=1) { dp[i][i][0]=b[i]=a[i]-'0'; } } int la; for(int i=n;i>=1;i--) { la=10; for(int j=i+1;j<=n;j++) { for(int k=i;k<j;k++) { dp[i][j][0]=dp[i][k][0]*la+dp[k+1][j][0]; } } } for(int i=n-1;i>=1;i--) for(int j=i+1;j<=n;j++) for(int k=1;k<=(j-i);k++) for(int l=i;l<j;l++) { for(int m=0;k-1-m>=0;m++) dp[i][j][k]=max(dp[i][l][m]*dp[l+1][j][k-1-m], max(dp[i][l][k-1-m]*dp[l+1][j][m], dp[i][j][k])); } printf("%d\n",dp[1][n][k]);}
wzhd(dalao)改后的4 for及总结
#include<cstdio>#include<iostream>#include<cstring>#include<cmath>using namespace std;int n,k,dp[100][100][10],b[100];char a[100];int main(){ scanf("%d%d",&n,&k);getchar(); for(int i=0;i<=n;i++) { scanf("%c",&a[i]); if(i>=1) { dp[i][i][0]=b[i]=a[i]-'0'; // 拆成个位 : 第 i 位 } } int la = 10; for(int i=n;i>=1;i--) //因为我们每次向后去找他的下一位组合起来,因此倒序。 for(int j=i+1;j<=n;j++)//先组成2位数,依次增加。 dp[i][j][0]=dp[i][j-1][0]*la+dp[j][j][0]; for(int i=n-1;i>=1;i--) for(int j=i+1;j<=n;j++)//同上枚举 for(int m=1;m<=k;m++)// times of‘*’ for(int l=i;l<=j;l++)//枚举断点 { //枚举左右两区间各有几个“*”。 //其实只要固定左边是1其实都会枚举到的 //方便叙述 (1 * 2 * 3) * 4 /(1 * 2)* (3 * 4) 显然重了, dp[i][j][m]=max(dp[i][l][m-1]*dp[l+1][j][0],dp[i][j][m]); } printf("%d\n",dp[1][n][k]);}
对于石子归并,因为合并的顺序不同会产生不同的结果。
所以石子归并不能规定最后一个合并的对象。
而是将左边合并的结果与右边合并的结果进行合并。
所以我们合并的时候,需要右边的合并结果。
换句话说,我们需要枚举起点而使得我们可以访问到右边的区间。
而乘积最大中,由于乘法满足交换律,我们可以规定最后一个乘的对象。
假设我们规定右边不进行乘法运算,只是将左边的区间乘右边的区间组成的一个数。
而这个数是预处理好的。
由于我们只是将左边合并的结果与右边这一个数合并。
所以我们需要的条件只是从1~i的合并结果,故不需要枚举起点。
总结,合并区间的时候我们并不能盲目地去合并这个区间,而是先观察合并的顺序对合并的结果是否有影响。是否我们需要处理出每一个区间的结果。
记忆化搜索
滑雪
//开全局变量时要慎重,回溯时可能出错 #include<cstdio> #include<iostream>#include<cstring>using namespace std;int n,m;const int maxn=1050;int dp[maxn][maxn],mp[maxn][maxn];int nx[]={0,0,1,-1};int ny[]={1,-1,0,0};int dfs(int x,int y){ if(dp[x][y]) return dp[x][y]; int ans=1; for(int i=0;i<4;i++) { int tx=nx[i]+x,ty=ny[i]+y; if(tx<=n&&tx>=1&&ty<=m&&ty>=1) { if(mp[tx][ty]<mp[x][y]) { ans=max(ans,dfs(tx,ty)+1); } } } dp[x][y]=ans; return ans;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&mp[i][j]); } int ans1=1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ans1=max(ans1,dfs(i,j)); cout<<ans1;}
方案数
传球游戏
#include<cstdio>#include<iostream>using namespace std;int n,m;int dp[200][200];int main(){ cin>>n>>m; dp[0][1]=1; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { if(j==1) dp[i][j]=dp[i-1][n]+dp[i-1][2]; else if(j==n) dp[i][j]=dp[i-1][n-1]+dp[i-1][1]; else dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1]; } cout<<dp[m][1];}
摆花
二维
#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n,m;int a[108],f[108][108];int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=0;i<=a[1];i++) f[1][i]=1; for(int i=2;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=a[i];k++) if(j-k>=0) f[i][j]=(f[i][j]+f[i-1][j-k])%1000007; printf("%d",f[n][m]);}
一维
#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n,m;int a[108],f[108];int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); f[0]=1; for(int i=1;i<=n;i++) for(int j=m;j>=1;j--) for(int k=1;k<=a[i];k++)//**从1开始的原因:自己想 if(j-k>=0) f[j]=(f[j]+f[j-k])%1000007; printf("%d",f[m]);}
未完待续
阅读全文
0 0
- 复习/学习DP的长记
- DP复习
- dp复习
- 初学者的学习复习
- C/C++复习:不等长字符串的排序(1)
- NOIP专题复习(三) 状压DP学习笔记
- 【NOIp复习】dp复习列表
- ffmpeg的复习、学习、总结
- 待学习&复习的内容
- 初学者的学习复习day_2
- 初学者的学习复习day_4
- 初学者的学习复习_day5
- 初学者的学习复习day_6
- 初学者的学习复习day_7
- 树形dp复习
- 单行dp复习hdu1087
- POJ 2559 dp 【复习】
- 蓝桥杯 结点选择 By Assassin 简单的树形dp复习
- Linux嵌入式学习的五点建议
- 考试总结7
- 洛谷P2285 [HNOI2004]打鼹鼠 (BZOJ 1207)
- jQuery,ajax的'数组传值'与'接收数组'实例应用(SSM框架)
- Yarn源码分析之事件异步分发器AsyncDispatcher
- 复习/学习DP的长记
- spring cloud-添加Hystrix Dashboard监控到系统中
- 洛谷P1049 装箱问题(DP, 0-1背包)
- Ubuntu安装VMware tools,以及虚拟机安装的Ubuntu如何实现全屏
- 使用PullToRefreshScrollView
- 关于图像处理中的插值和旋转
- 创建用户定义的数据类型
- JSON 与 对象 、集合 之间的转换
- jQuery的一些父级、子级、同级选择器。