一:入门
- 快速构建横纵坐标,集合划分
dp问题的数组下标都是从1开始
dp问题时间复杂度的分析
状态的数量 * 算每一个状态需要的计算量
#include <iostream>#include <algorithm>using namespace std;const int N = 510, INF = 0x3f3f3f3f;int f[N][N], a[N][N];int n;int main(){ios::sync_with_stdio(false);cin >> n;for (int i = 1; i <= n; i++) // 三角形输入的抽象for (int j = 1; j <= i; j++)cin >> a[i][j];for (int i = 0; i <= n; i++) // 因为状态转移方程用到了一些没有输入的地方,//右上角和左上角的边界,需要预先变成无穷小,这样就防止三角形的元素是负数,选择了不存在的路径for (int j = 0; j <= i + 1; j++)f[i][j] = -INF;f[1][1] = a[1][1]; // 必须初始化,不然没有初始值,所有的都是很小很小的负数for (int i = 2; i <= n; i++) // 必须从2开始,否则,后面的滚动数组全部都是负数for (int j = 1; j <= i; j++)f[i][j] = max(f[i - 1][j - 1] + a[i][j], f[i - 1][j] + a[i][j]);int res = -INF;for (int i = 1; i <= n; i++) res = max(res, f[n][i]); // 最后一行的n存储着所有最大的路径值,需要选择最大cout << res << endl;return 0;}
二:最长上升子序列

1,2,5,6就是四个上升子序列。
#include <iostream>#include <algorithm>using namespace std;const int N = 1010;int f[N], a[N];int n;int main(){ios::sync_with_stdio(false);cin >> n;for (int i = 1; i <= n; i++)cin >> a[i];for (int i = 1; i <= n; i++){f[i] = 1; // 防止前面没有小于a[i]for (int j = 1; j <= i; j++)if (a[j] < a[i]) // 只要有小于a[i]的就更新, 一定是需要比较的f[i] = max(f[i], f[j] + 1);}int res = 0;for (int i = 1; i <= n; i++)res = max(res, f[i]);cout << res << endl;return 0;}
三:最长公共子序列长度
- https://www.acwing.com/solution/content/8111/解释的很好
- y总解释的很详细,中间01的时候,代表b[j]一定是第二个字符串,但是01是这个意思,这句话也就体会出来f[i - 1][j]范围是更大的。

#include <iostream>#include <algorithm>using namespace std;const int N = 1010;char a[N], b[N];int f[N][N];int main(){ios::sync_with_stdio(false);int n, m;cin >> n >> m;cin >> a + 1 >> b + 1;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++){f[i][j] = max(f[i - 1][j], f[i][j - 1]);if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;}cout << f[n][m] << endl;return 0;}
