线性DP
1.数字三角形 给定一个由数字组成的三角形 求出从最下面一层到顶点的最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <bits/stdc++.h> using namespace std;const int maxn = 10005 ;int tr[maxn][maxn],dp[maxn][maxn];int dfs (int x,int y) { if (dp[x][y]!=-1e9 ) return dp[x][y]; dp[x][y] = max ( dfs (x+1 ,y), dfs (x+1 ,y+1 ) ) + tr[x][y]; return dp[x][y]; }int main () { int n;cin >> n; for (int i = 1 ;i<=n;i++) { for (int j = 1 ;j<=i;j++){ cin >> tr[i][j]; if (i==n) dp[i][j] = tr[i][j]; else dp[i][j] = -1e9 ; } } for (int i = n-1 ;i>=1 ;i--){ for (int j = 1 ;j<=i;j++){ dp[i][j] = max (dp[i+1 ][j],dp[i+1 ][j+1 ]) + tr[i][j]; } } cout << dp[1 ][1 ]; return 0 ; }
2.最长不下降子序列 (LIS Longest Increasing Sequence)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std;int main () { int N;cin >> N; vector<int >vi; int dp[1006 ]; int ans = -1 ; for (int i = 0 ;i<N;i++){ int t;cin >> t; vi.push_back (t); } for (int i = 0 ;i<N;i++){ dp[i] = 1 ; for (int j = 0 ;j<i;j++){ if (vi[j]<vi[i]){ dp[i] = max (dp[i],dp[j] + 1 ); } } ans = max (ans,dp[i]); } cout << ans; }
3.最长公共子序列问题 LCS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <bits/stdc++.h> using namespace std;int dp[1006 ][1006 ];int main () { int n,m; cin >> n >> m; string a,b; cin >> a >> b; a = "0" +a; b = "0" +b; for (int i = 1 ;i<=n;i++){ for (int j = 1 ;j<=m;j++){ if (a[i]==b[j]){ dp[i][j] = dp[i-1 ][j-1 ] + 1 ; }else { dp[i][j] = max (dp[i-1 ][j],dp[i][j-1 ]); } } } cout << dp[n][m]; return 0 ; }
4.编辑距离问题 通过删除 添加 替换三种操作 将两个字符串变成完全一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <bits/stdc++.h> using namespace std;int dp[1006 ][1006 ];int main () { int n,m; string a,b; cin >> n >> a >> m >> b; a = "0" + a; b = "0" + b; for (int i = 0 ;i<=max (n,m);i++) { dp[i][0 ] = i; dp[0 ][i] = i; } for (int i = 1 ;i<=n;i++){ for (int j = 1 ;j<=m;j++){ dp[i][j] = min (dp[i][j-1 ],dp[i-1 ][j]) + 1 ; if (a[i]==b[j]) dp[i][j] = min (dp[i][j],dp[i-1 ][j-1 ]); else { dp[i][j] = min (dp[i-1 ][j-1 ]+1 ,dp[i][j]); } } } cout << dp[n][m]; return 0 ; }
5.合并石子,和哈夫曼的规则相似,只不过,每次合并的只能是相邻的两队石子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <bits/stdc++.h> using namespace std;const int N = 305 ;int a[N],pre[N];int dp[N][N];int dfs (int l,int r) { if (l==r) return 0 ; int v = dp[l][r]; if (v!=-1 ) return v; v = 1e8 ; for (int k = l;k<r;k++){ v = min (v,dfs (l,k)+dfs (k+1 ,r)+pre[r]-pre[l-1 ]); } dp[l][r] = v; return v; }int main () { int N; cin >> N; for (int i = 0 ;i<=N;i++){ for (int j = 0 ;j<=N;j++){ dp[i][j] = -1 ; } } for (int i = 1 ;i<=N;i++){ cin >> a[i]; pre[i] = pre[i-1 ]+a[i]; } cout << dfs (1 ,N); return 0 ; }
背包问题
01背包,给定N个物品,每个物品具有价值和重量,要求你在其中任选多个,在总重量不大于V的前提下,价值最大
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <bits/stdc++.h> using namespace std;int v[1006 ],w[1006 ];int dp[1006 ][1006 ];int main () { int N,V;cin >> N >> V; for (int i = 1 ;i<=N;i++) { cin >> v[i] >> w[i]; } for (int i = 1 ;i<=N;i++){ for (int j = 1 ;j<=V;j++){ if (j>=v[i])dp[i][j] = max (dp[i-1 ][j],dp[i-1 ][j-v[i]] + w[i]); else dp[i][j] = dp[i-1 ][j]; } } cout << dp[N][V]; return 0 ; }
完全背包,每个物品可以选择的数量是任意多个的,要求符合条件的最大价值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <bits/stdc++.h> using namespace std;int v[1006 ],w[1006 ];int dp[1006 ][1006 ];int main () { int N,V;cin >> N >> V; for (int i = 1 ;i<=N;i++) { cin >> v[i] >> w[i]; } for (int i = 1 ;i<=N;i++){ for (int j = 1 ;j<=V;j++){ if (j>=v[i])dp[i][j] = max (dp[i-1 ][j],dp[i][j-v[i]] + w[i]); else dp[i][j] = dp[i-1 ][j]; } } cout << dp[N][V]; return 0 ; }
应用:知道存钱罐的总重量和里面各类钱币的质量,就可以得到这个存钱罐里面最多最少有多少钱
多重背包I,限制了一些物品的最大数量,求在这个前提下的最大价值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <bits/stdc++.h> using namespace std;int v[1006 ],w[1006 ],s[1006 ];int dp[1006 ][1006 ];int main () { int N,V;cin >> N >> V; for (int i = 1 ;i<=N;i++) { cin >> v[i] >> w[i] >> s[i]; } for (int i = 1 ;i<=N;i++){ for (int j = 1 ;j<=V;j++){ for (int k = 0 ;k<=s[i];k++){ if (j>=k*v[i]){ dp[i][j] = max (dp[i][j],dp[i-1 ][j-v[i]*k] + w[i]*k); } } } } cout << dp[N][V]; return 0 ; }
分组背包问题 分成一些组别 每一组至多只能选择一种商品 求最大的价值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <bits/stdc++.h> using namespace std;int v[1006 ][1006 ],w[1006 ][1006 ],s[1006 ];int dp[1006 ][1006 ];int main () { int N,V;cin >> N >> V; for (int i = 1 ;i<=N;i++) { cin >> s[i]; for (int j = 0 ;j<s[i];j++){ cin>>v[i][j] >> w[i][j]; } } for (int i = 1 ;i<=N;i++){ for (int j = 1 ;j<=V;j++){ for (int k = 0 ;k<=s[i];k++){ if (j>=v[i][k]){ dp[i][j] = max (dp[i][j],dp[i-1 ][j-v[i][k]] + w[i][k]); } } } } cout << dp[N][V]; return 0 ; }