动态规划
1、经典问题:(数塔)自顶向下分析,自底向上计算

#include <iostream> #include <cmath> using namespace std; int a[355][355], dp[355][355]; int n; int main() { while(cin >> n) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= i; j++) { cin >> a[i][j]; } } for(int i = 1; i <= n; i++) dp[n][i] = a[n][i]; for(int i = n-1; i >= 1; i--) { for(int j = 1; j <= i; j++) { dp[i][j] = a[i][j] + max(dp[i+1][j], dp[i+1][j+1]); } } cout << dp[1][1] << endl; } return 0; }
2、最长有序(递增或递减)序列
核心算法:
for(int i = 1; i <= n; i++) { maxtemp = 0; for(int j = 1; j <= i; j++) { if(a[j] < a[i] && maxtemp < dp[j]) maxtemp = dp[j]; } dp[i] = maxtemp+1; }
例题:
1)、poj2533Longest Ordered Subsequence

#include <iostream> #include <cstdio> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; int main() { int n; int a[1100], dp[1100]; while(cin >> n) { for(int i = 1; i <= n; i++) cin >> a[i]; int len = 0; int maxtemp; for(int i = 1; i <= n; i++) { dp[i] = 1; maxtemp = 0; for(int j = 1; j <= i; j++) { if(a[j] < a[i] && maxtemp < dp[j]) maxtemp = dp[j]; } dp[i] = maxtemp+1; if(len < dp[i]) len = dp[i]; } cout << len << endl; } return 0; } ---------------------------------------------------------------------- #include <iostream> #include <cstdio> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; int main() { int n; int a[1100], dp[1100]; while(cin >> n) { for(int i = 1; i <= n; i++) cin >> a[i]; int len = 0; for(int i = 1; i <= n; i++) { dp[i] = 1; for(int j = 1; j <= i; j++) { if(a[j] < a[i] && dp[i] < dp[j]+1) dp[i] = dp[j]+1; } if(len < dp[i]) len = dp[i]; } cout << len << endl; } return 0; }

#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <algorithm> using namespace std; int main() { int n; double a[1001]; int dp[1001], opt[1001]; while(cin >> n) { for(int i = 1; i <= n; i++) cin >> a[i]; dp[1] = 1; int maxtemp; for(int i = 2; i <= n; i++) { maxtemp = 0; for(int j = 1; j < i; j++) { if(a[i] > a[j] && maxtemp < dp[j]) maxtemp = dp[j]; } dp[i] = maxtemp + 1; } opt[n] = 1; for(int i = n-1; i >= 1; i--) { maxtemp = 0; for(int j = n; j > i; j--) { if(a[i] > a[j]&&maxtemp < opt[j]) { maxtemp = opt[j]; } } opt[i] = maxtemp+1; } int maxn = 0; for(int i = 1; i <= n; i++) { for(int j = i+1; j <= n; j++) { if(maxn < dp[i] + opt[j]) maxn = dp[i] + opt[j]; } } cout << n-maxn << endl; } return 0; }
3、最长公共子序列
核心算法
for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dp[i][j] = max(dp[i][j-1], dp[i-1][j]); if(a[i] == b[j]) { dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1); } } }

#include <iostream> #include <cstdio> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #define max(a,b) (a>b?a:b) using namespace std; short int dp[5001][5001]; int main() { int n; int a[5001], b[5001]; char str; cin >> n; getchar(); for(int i = 1; i <= n; i++) { scanf("%c", &str); a[i] = str; b[n-i+1] = str; } for(int i = 0; i <= n; i++) { dp[i][0] = 0; dp[0][i] = 0; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dp[i][j] = max(dp[i][j-1], dp[i-1][j]); if(a[i] == b[j]) { dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1); } } } int maxn = dp[n][n]; printf("%d\n", n-maxn); return 0; }
此题还要注意short int 数组的运用
short int 为2字节 int为4字节 使用short int 可以节省空间
4、形如 E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij}

#include <iostream> #include <cstdio> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #define INF 999999999 using namespace std; int g['T'+1]['T'+1]; int dp[110][110]; void init() { g['A']['A'] = 5; g['C']['C'] = 5; g['G']['G'] = 5; g['T']['T'] = 5; g['-']['-'] = INF; g['A']['C'] = g['C']['A'] = -1; g['A']['G'] = g['G']['A'] = -2; g['A']['T'] = g['T']['A'] = -1; g['A']['-'] = g['-']['A'] = -3; g['C']['G'] = g['G']['C'] = -3; g['C']['T'] = g['T']['C'] = -2; g['C']['-'] = g['-']['C'] = -4; g['G']['T'] = g['T']['G'] = -2; g['G']['-'] = g['-']['G'] = -2; g['T']['-'] = g['-']['T'] = -1; return; } int maxn(int a, int b, int c) { int temp = max(a, b); temp = max(temp, c); return temp; } int main() { int T; int lena, lenb; char s2[110], s1[110]; cin >> T; while(T--) { init(); cin >> lena; cin >> s1; cin >> lenb; cin >> s2; dp[0][0] = 0; for(int i = 1; i <= lena; i++) dp[i][0] = dp[i-1][0] + g[s1[i-1]]['-']; for(int i = 1; i <= lenb; i++) dp[0][i] = dp[0][i-1] + g['-'][s2[i-1]]; for(int i = 1; i <= lena; i++) { for(int j = 1; j <= lenb; j++) { int temp1 = dp[i-1][j] + g[s1[i-1]]['-']; int temp2 = dp[i][j-1] + g['-'][s2[j-1]]; int temp3 = dp[i-1][j-1] + g[s1[i-1]][s2[j-1]]; dp[i][j] = maxn(temp1, temp2, temp3); } } cout << dp[lena][lenb] << endl; } return 0; }
5、形如 E[j]=opt{D+w(i,j)}

#include <iostream> #include <cstdio> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; int main() { int T; cin >> T; while(T--) { int n; cin >> n; int *a = new int[n+1]; int *p = new int[n+1]; int *dp = new int[n+1]; int *sum = new int[n+1]; sum[0] = 0; for(int i = 1; i <= n; i++) { cin >> a[i] >> p[i]; sum[i] = sum[i-1] + a[i]; } dp[0] = 0; for(int i = 1; i <= n; i++) { dp[i] = (a[i]+ 10)*p[i] + dp[i-1]; for(int j = 0; j < i; j++) { dp[i] = min(dp[i], dp[j] + (sum[i]-sum[j]+10)*p[i]); } } cout << dp[n] << endl; delete a, p, dp, sum; } return 0; }

#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> using namespace std; int main() { int W,L; while(cin >> W >> L) { int* dp = new int[L+1]; char* str = new char[L+1]; string* s = new string[W]; cin >> str; for(int i = 0; i < W; i++) cin >> s[i]; dp[L] = 0; for(int i = L-1; i >= 0; i--) { dp[i] = dp[i+1] + 1; for(int j = 0; j < W; j++) { int len = s[j].length(); if(len <= L-i && s[j][0] == str[i]) { int pm = i; int pd = 0; while(pm < L) { if(s[j][pd] == str[pm++]) pd++; if(pd == len) { dp[i] = min(dp[i], dp[pm]+(pm-i)-len); break; } } } } } cout << dp[0] << endl; } return 0; }
注意:数组的开法
int* dp = new int[L+1]; char* str = new char[L+1]; string* s = new string[W];
在poj上提交时,如果直接开就会产生错误,关于这点我也不知道为什么。。。以后知道了在做补充吧
6、谨记 DP是转态的转移

#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <cstdlib> using namespace std; int dp[21][15001]; int main() { int C, G; int a[25], g[25]; while(cin >> C >> G) { for(int i = 1; i <= C; i++) cin >> a[i]; for(int i = 1; i <= G; i++) cin >> g[i]; memset(dp, 0, sizeof(dp)); dp[0][7500] = 1; for(int i = 1; i <= G; i++) { for(int j = 0; j <= 15000; j++) { if(dp[i-1][j]) for(int k = 1; k <= C; k++) dp[i][j+g[i]*a[k]] += dp[i-1][j]; } } cout << dp[G][7500] << endl; } return 0; }
7、背包问题
0/1背包,完全背包,多重背包模板如下
int dp[100005], vol[101], num[101]; int n, v; void ZeroOnePack(int val, int vol) { for(int i = v; i >= vol; i--) dp[i] = max(dp[i], dp[i-vol]+val); } void CompletePack(int val, int vol) { for(int i = vol; i <= v; i++) dp[i] = max(dp[i], dp[i-vol]+val); } void MultiplePack(int val, int vol, int cnt) { if(val*vol >= v) { CompletePack(val, vol); return; } int k = 1; while(k < cnt) { ZeroOnePack(k*val, k*vol); cnt -= k; k = k*2; } ZeroOnePack(cnt*val, cnt*vol); }

#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> using namespace std; int dp[100005], vol[101], num[101]; int n, v; void ZeroOnePack(int val, int vol) { for(int i = v; i >= vol; i--) dp[i] = max(dp[i], dp[i-vol]+val); } void CompletePack(int val, int vol) { for(int i = vol; i <= v; i++) dp[i] = max(dp[i], dp[i-vol]+val); } void MultiplePack(int val, int vol, int cnt) { /* if(val*vol >= v) { CompletePack(val, vol); return; }*/ int k = 1; while(k < cnt) { ZeroOnePack(k*val, k*vol); cnt -= k; k = k*2; } ZeroOnePack(cnt*val, cnt*vol); } int main() { while(scanf("%d%d", &v, &n) != EOF) { for(int i = 0; i <= v; i++) dp[i] = 0; for(int i = 0; i < n; i++) { scanf("%d%d", &num[i], &vol[i]); MultiplePack(vol[i], vol[i], num[i]); } printf("%d\n", dp[v]); } return 0; }