DP
来源:互联网 发布:荣誉证书在线制作软件 编辑:程序博客网 时间:2024/06/06 03:25
动态规划
①符合无后效性:不管之前的这个状态是如何的到的(之前计算的最优不会因为后面的数据而没用)
②最优子结构:每个阶段的最优状态可以从之前的某个阶段的某个或某些状态直接得到
设计步骤:
一、分析子问题
二、做表
自己总结的,不供参考
类型:
数塔
http://acm.hdu.edu.cn/showproblem.php?pid=2084
#include<iostream>#include<cstdio>using namespace std;#define maxn 105int f[maxn][maxn];int main(){ int t,n,i,j; scanf("%d",&t); while(t--){ scanf("%d",&n); for(i = 0; i < n; ++i){ for(j = 0; j <= i; ++j){ scanf("%d",&f[i][j]); } } for(i = n-2; i >= 0; --i){ for(j = 0; j <= i; ++j){ f[i][j] = (f[i][j]+f[i+1][j]) >= (f[i][j]+f[i+1][j+1]) ? (f[i][j]+f[i+1][j]): (f[i][j]+f[i+1][j+1]); } } printf("%d\n",f[0][0]); }}
空间优化
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#define maxn 105using namespace std;int dp[maxn], f[maxn][maxn];int main(){ int t, n, i, j; scanf("%d",&t); while(t--){ scanf("%d",&n); memset(dp, 0, sizeof(dp)); for(i = 1; i <= n; ++i){ for(j = 1; j <= i; ++j) scanf("%d",&f[i][j]); } for(i = n; i > 0; --i){ for(j = 1; j <= i; ++j){ dp[j] = max(dp[j],dp[j+1]) + f[i][j]; } } printf("%d\n",dp[1]); }}
最大连续子序列和(要求输出起点终点)
http://acm.hdu.edu.cn/showproblem.php?pid=1003
#include<iostream>#include<cstdio>#include<algorithm>using namespace std;#define INF 0x3f3f3f3fint f[100050];int main(){ int t, i, j, sum, start, end, flag, ans, n, test; scanf("%d",&test); for(n = 1; n <= test; ++n){ scanf("%d",&t); flag = 1; for(i = 1; i <= t; ++i){ scanf("%d",&f[i]); } ans = -INF; sum = 0; j = 1;//记录起点 for(i = 1; i <= t; ++i){ sum += f[i]; if(sum > ans){ //更新最大值 ans = sum; start = j; end = i; } if(sum < 0){ //更新起点 sum = 0; j = i + 1; } } printf("Case %d:\n%d %d %d\n",n,ans, start, end); if(n != test) printf("\n"); }}
最长上升子序列
http://poj.org/problem?id=2533
需打印序列(并查集):
#include<iostream>#include<cstdio>#include<stack> using namespace std;#define max 1050int a[max], dp[max], f[max]; //dp[i]:储存以a[i]为结尾的最大上升子序列 f[i]:存储父结点 //stack<int> num;int main(){ int n, i, j, maxn, index; //index记录dp最大时的下标 scanf("%d", &n); for(i = 1; i <= n; ++i){ scanf("%d", &a[i]); } dp[1] = 1; //f[1] = 1; int ans = 1; //index = 1; for(i = 2; i <= n; ++i){ maxn = 0; //f[i] = i; for(j = 1; j < i; ++j){ if(a[i] > a[j] && maxn < dp[j]){ maxn = dp[j]; //f[i] = j; } } dp[i] = maxn + 1; if(ans < dp[i]){ //index = i; ans = dp[i]; } } printf("%d\n",ans); //i = index; //while(f[i] != i){ // num.push(a[i]); // i = f[i]; //} //num.push(a[i]); //while(!num.empty()){ // printf("%d ",num.top()); // num.pop(); //} printf("\n");}
不需要打印序列(二分查找)(nlogn):
https://cn.vjudge.net/problem/HDU-1025
从1~n遍历,dp[len]存储长度为len的最长上升子序列的结尾。现在用数组f存储序列, 若dp[len] <= f[i]则把f[i]加入到dp[++len],相反遍历数组dp,找到一个dp[j] < f[i] < dp[j+1], 然后dp[j+1] = f[i]。(其实就是把大的数加入到dp中, 小的数替换前面大的, 替换过程中的查找用二分查找@_@)
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 500050#define INF 0x3f3f3f3fint dp[maxn], f[maxn];int main(){ int i , j, k, n, x, y, ans, times = 0, left, right, mid, temp; while(~scanf("%d", &n)){ for(i = 1; i <= n; ++i){ scanf("%d %d", &x, &y); f[x] = y; } printf("Case %d:\n", ++times); memset(dp, 0, sizeof(dp)); ans = 1; dp[1] = f[1]; for(i = 2; i <= n; ++i){ if(dp[ans] <= f[i]){ dp[++ans] = f[i]; } else{ left = 1; right = ans; while(right >= left){ mid = (left + right)/2; if(dp[mid] > f[i]) right = mid - 1; else left = mid + 1; } dp[left] = f[i]; } } if(ans != 1) printf("My king, at most %d roads can be built.\n\n",ans); else printf("My king, at most 1 road can be built.\n\n"); }}
最长公共子序列
http://acm.hdu.edu.cn/showproblem.php?pid=1159
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 1050char str1[maxn], str2[maxn];int dp[maxn][maxn];//dp[i][j]:str1[i]与str2[j]为结尾的最大公共子序列int main(){ while(~scanf("%s %s", str1, str2)){ int len1 = strlen(str1), len2 = strlen(str2), ans = 0; for(int i = 1; i <= len1; ++i){ for(int j = 1; j <= len2; ++j){ if(str1[i-1] == str2[j-1]){ dp[i][j] = dp[i-1][j-1] + 1; } else{ dp[i][j] = max(dp[i-1][j], dp[i][j-1]); } if(ans < dp[i][j]){ ans = dp[i][j]; } } } printf("%d\n", ans); }}
01背包
http://acm.hdu.edu.cn/showproblem.php?pid=2602
二维( j 必须从 0 开始):
#include<iostream>#include<cstdio>#include <algorithm> #include<cstring>#define maxn 1050using namespace std;int dp[maxn][maxn];int main(){ int t, n, v, i, j; scanf("%d", &t); while(t--){ int va[1050] = {0}, vo[1050] = {0}; memset(dp,0,sizeof(dp)); scanf("%d %d", &n, &v); for(i = 1; i <= n; ++i){ scanf("%d", &va[i]); } for(i = 1; i <= n; ++i){ scanf("%d", &vo[i]); } for(i = 1; i <= n; ++i){ for(j = 0; j <= v; ++j){ if(vo[i] <= j) dp[i][j] = max(dp[i-1][j],(dp[i-1][j-vo[i]]+va[i])); else{ dp[i][j] = dp[i-1][j]; } } } printf("%d\n",dp[n][v]); }}
一维:
#include<iostream>#include<cstdio>#include <algorithm> using namespace std;int main(){ long long t, n, v, i, j; scanf("%lld", &t); while(t--){ long long va[1050] = {0}, vo[1050] = {0}, dp[1050] = {0}; scanf("%lld %lld", &n, &v); for(i = 1; i <= n; ++i){ scanf("%lld", &va[i]); } for(i = 1; i <= n; ++i){ scanf("%lld", &vo[i]); } for(j = 1; j <= n; ++j){ for(i = v; i >= vo[j]; --i){ dp[i] = max(dp[i],(dp[i-vo[j]] + va[j])); //cout << "j :" << j << " i: " << i << " dp:"<< dp[i]<<endl; } } printf("%lld\n",dp[v]); }}
多重背包:
http://acm.hdu.edu.cn/showproblem.php?pid=2844
二进制优化+拆解(要求出具体最大值):
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 100050#define INF 0x3f3f3f3fint dp[maxn], a[maxn], c[maxn];int main(){ int n, m, i, j, k, ans; while(~scanf("%d %d",&n, &m) && (n!=0||m!=0)){ memset(dp, -INF,sizeof(dp)); memset(a, 0,sizeof(a)); memset(c, 0,sizeof(c)); for(i = 1; i <= n; ++i){ scanf("%d", &a[i]); } for(j = 1; j <= n; ++j){ scanf("%d", &c[j]); } dp[0] = 1; for(i = 1; i <= n; ++i){ if(a[i] * c[i] >= m){ for(j = a[i]; j <= m; ++j){ dp[j] = max(dp[j], dp[j-a[i]] + a[i]); } } else{ for(k = 1; k <= c[i]; k *= 2){ for(j = m; j >= a[i] * k; --j){ dp[j] = max(dp[j], dp[j-a[i]*k]+a[i]*k); } c[i] -= k; } if(c[i] > 0){ for(j = m; j >= a[i] * c[i]; --j){ dp[j] = max(dp[j], dp[j-a[i]*c[i]]+a[i]*c[i]); } } } } ans = 0; for(j = 1; j <= m; ++j){ if(dp[j] >= 0){ ans++; } } printf("%d\n", ans); //printf("%d\n",dp[m]); }}
只要求出最大组合数:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 100050#define INF 0x3f3f3f3fint dp[maxn], num[maxn], a[maxn], c[maxn];int main(){ int n, m, i, j, k, ans; while(~scanf("%d %d",&n, &m) && (n!=0||m!=0)){ memset(dp, 0,sizeof(dp)); for(i = 1; i <= n; ++i){ scanf("%d", &a[i]); } for(j = 1; j <= n; ++j){ scanf("%d", &c[j]); } dp[0] = 1; ans = 0; for(i = 1; i <= n; ++i){ memset(num, 0, sizeof(num)); for(j = a[i]; j <= m; ++j){ if(!dp[j] && c[i] > num[j - a[i]]&&dp[j-a[i]]){ num[j] = num[j-a[i]] + 1; dp[j] = 1; ++ans; } } } printf("%d\n", ans); }}
完全背包:
http://acm.hdu.edu.cn/showproblem.php?pid=1114
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 10050#define INF 0x3f3f3f3f int dp[maxn], value[1000], weight[1000];int main(){ int t, e, f, i, j, w, n, k, ans, less; scanf("%d",&t); while(t--){ scanf("%d %d",&e, &f); w = f - e; scanf("%d",&n); for(i = 0; i < n; ++i){ scanf("%d %d",&value[i], &weight[i]); } memset(dp,INF,sizeof(dp)); dp[0] = 0; for(i = 0; i < n; ++i){ for(j = weight[i]; j <= w; ++j){ dp[j] = min(dp[j],dp[j-weight[i]]+value[i]); } } if(dp[w] >= INF) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[w]); }}