线性dp
1.数字三角形。acwing 898.

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 520,INF = 1e9; 5 int n; 6 int a[N][N]; //表示每一个点 7 int f[N][N]; //表示状态 8 9 int main() 10 { 11 cin >> n; 12 for (int i = 1; i <= n; i ++ ) 13 for (int j = 1; j <= i; j ++ ) 14 cin >> a[i][j]; 15 16 for (int i = 0; i <= n; i ++ ) 17 for (int j = 0; j <= i + 1; j ++ ) //每行多初始化一个,表示三角形最右边的右上角表示负无穷 18 f[i][j] = -INF; //避免处理边界情况,先将状态初始化为负无穷 19 20 f[1][1] = a[1][1]; 21 22 for (int i = 2; i <= n; i ++ ) 23 for (int j = 1; j <= i; j ++ ) 24 f[i][j] = max(f[i - 1][j - 1] + a[i][j],f[i - 1][j] + a[i][j]); 25 26 int res = -INF; 27 for (int i = 1; i <= n; i ++ ) //枚举三角形底边终点的状态的最大值 28 res = max(res,f[n][i]); 29 30 cout << res << endl; 31 32 return 0; 33 }
2.最长上升子序列

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1010; 5 6 int n; 7 int a[N],f[N]; 8 9 int main () 10 { 11 scanf("%d", &n); 12 for (int i = 1; i <= n; i ++ ) cin >> a[i]; 13 14 for (int i = 1; i <= n; i ++ )//从前往后计算每一个状态 15 { 16 f[i] = 1;//表示以i结尾的子序列的长度为1 只有a[i]这一个数 17 for (int j = 1; j < i; j ++ ) 18 if(a[j] < a[i]) f[i] = max(f[i],f[j] + 1); 19 } 20 21 int res = 0; 22 for (int i = 1; i <= n; i ++ ) res = max(res,f[i]); //枚举所有中点; 23 24 cout << res << endl; 25 26 return 0; 27 }
3.最长公共子序列

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n,m; 5 const int N = 1010; 6 char a[N],b[N]; 7 int f[N][N]; //状态表示为第一个字符串的前i个和第二个字符串的前j个构成的公共子序列 8 9 int main() 10 { 11 scanf("%d %d", &n, &m); 12 13 for (int i = 1; i <= n; i ++ ) cin >> a[i]; 14 for (int i = 1; i <= m; i ++ ) cin >> b[i]; 15 16 for (int i = 1; i <= n; i ++ ) 17 for (int j = 1; j <= m; j ++ ) 18 { 19 f[i][j] = max(f[i - 1][j],f[i][j - 1]); //01和10状态包含了00状态 20 //01和10分别表示a字符串的最后一个不选以及b的最后一个选 21 if(a[i] == b[j]) f[i][j] = max(f[i][j],f[i - 1][j - 1] + 1); 22 } 23 24 cout << f[n][m] << endl; 25 }
精简版:二分优化

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 100010; 5 6 int n; 7 int a[N]; //表示数组 8 int q[N]; //表示所有不同长度的上升子序列的结尾的最小值 9 10 int main () 11 { 12 scanf("%d", &n); 13 for (int i = 0; i < n; i ++ ) cin >> a[i]; 14 15 int len = 0; 16 q[0] = -2e9; 17 18 for (int i = 0; i < n; i ++ ) 19 { 20 int l = 0,r = len; 21 while(l < r) 22 { 23 int mid = l + r + 1 >> 1; 24 if(q[mid] < a[i]) l = mid; 25 else r = mid - 1; 26 } 27 len = max(len,r + 1); 28 q[r + 1] = a[i]; 29 } 30 31 cout << len << endl; 32 return 0; 33 }
4.最短编辑距离

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1010; 5 6 int n,m; 7 char a[N],b[N]; 8 int f[N][N]; //集合表示 由a[1 ~ i]变成b[1 ~ j]的操作方式 9 10 int main() 11 { 12 scanf("%d%s", &n,a + 1); 13 14 scanf("%d%s", &m,b + 1); 15 16 for (int i = 0; i <= m; i ++ ) f[0][i] = i;//b的前i个字母和a的前0个字母匹配,只能将a的前i个字母增加i次 17 for (int i = 0; i <= n; i ++ ) f[i][0] = i;//a的前i个字母和b的前零个字母匹配,只能将a个i个字母删除i次 18 19 for (int i = 1; i <= n; i ++ ) 20 for (int j = 1; j <= m; j ++ ) 21 { 22 f[i][j] = min(f[i - 1][j] + 1,f[i][j - 1] + 1);//a字符的删和增的操作 23 if(a[i] == b[j]) f[i][j] = min(f[i][j],f[i - 1][j - 1]); //a字符的修改的操作 24 else f[i][j] = min(f[i][j],f[i - 1][j - 1] + 1); 25 } 26 27 cout << f[n][m] << endl;//把a的前n个字母变成b的前m个字母 28 29 return 0; 30 }
5.编辑距离

1 #include <algorithm> 2 #include <cstring> 3 #include <iostream> 4 5 using namespace std; 6 7 const int N = 15, M = 1e3 + 10; 8 9 int n, m; 10 char str[M][N]; 11 int dp[N][N]; 12 13 int edit_distance(char a[], char b[]) 14 { 15 int la = strlen(a + 1), lb = strlen(b + 1); 16 for (int i = 0; i <= lb; i++) { 17 dp[0][i] = i; 18 } 19 for (int i = 0; i <= la; i++) { 20 dp[i][0] = i; 21 } 22 for (int i = 1; i <= la; i++) { 23 for (int j = 1; j <= lb; j++) { 24 dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1); 25 dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + (a[i] != b[j])); 26 } 27 } 28 return dp[la][lb]; 29 } 30 31 int main() 32 { 33 cin >> n >> m; 34 for (int i = 0; i < n; i++) { 35 cin >> (str[i] + 1); 36 } 37 38 while (m -- ) { 39 int res = 0; 40 char s[N]; 41 int limit; 42 cin >> (s + 1) >> limit; 43 for (int i = 0; i < n; i ++) { 44 if (edit_distance(str[i], s) <= limit) { 45 res++; 46 } 47 } 48 cout << res << endl; 49 } 50 51 return 0; 52 }
6.整数划分

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n; 5 const int N = 1010,mod = 1e9 + 7; 6 int f[N]; 7 8 //可视为完全背包问题去解决 9 int main() 10 { 11 scanf("%d", &n); 12 13 f[0] = 1; 14 15 for (int i = 1; i <= n; i ++ ) 16 for (int j = i; j <= n; j ++ ) 17 f[j] = (f[j] + f[j - i]) % mod; 18 19 cout << f[n] << endl; 20 21 }
第二种方式

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n; 5 const int N = 1010,mod = 1e9 + 7; 6 int f[N][N]; //表示所有总和是i,并且恰好表示成J个数的方案 7 8 int main() 9 { 10 scanf("%d", &n); 11 12 f[0][0] = 1; 13 14 for (int i = 1; i <= n; i ++ ) 15 for (int j = 1; j <= i; j ++ ) 16 f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod; 17 18 int res = 0; 19 for (int i = 1; i <= n; i ++ ) res = (res + f[n][i]) % mod; 20 21 cout << res << endl; 22 return 0; 23 24 }