一:入门

  • 快速构建横纵坐标,集合划分

image.png

dp问题的数组下标都是从1开始

image.png

dp问题时间复杂度的分析

状态的数量 * 算每一个状态需要的计算量

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. const int N = 510, INF = 0x3f3f3f3f;
  5. int f[N][N], a[N][N];
  6. int n;
  7. int main()
  8. {
  9. ios::sync_with_stdio(false);
  10. cin >> n;
  11. for (int i = 1; i <= n; i++) // 三角形输入的抽象
  12. for (int j = 1; j <= i; j++)
  13. cin >> a[i][j];
  14. for (int i = 0; i <= n; i++) // 因为状态转移方程用到了一些没有输入的地方,
  15. //右上角和左上角的边界,需要预先变成无穷小,这样就防止三角形的元素是负数,选择了不存在的路径
  16. for (int j = 0; j <= i + 1; j++)
  17. f[i][j] = -INF;
  18. f[1][1] = a[1][1]; // 必须初始化,不然没有初始值,所有的都是很小很小的负数
  19. for (int i = 2; i <= n; i++) // 必须从2开始,否则,后面的滚动数组全部都是负数
  20. for (int j = 1; j <= i; j++)
  21. f[i][j] = max(f[i - 1][j - 1] + a[i][j], f[i - 1][j] + a[i][j]);
  22. int res = -INF;
  23. for (int i = 1; i <= n; i++) res = max(res, f[n][i]); // 最后一行的n存储着所有最大的路径值,需要选择最大
  24. cout << res << endl;
  25. return 0;
  26. }

二:最长上升子序列

image.png
1,2,5,6就是四个上升子序列。

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. const int N = 1010;
  5. int f[N], a[N];
  6. int n;
  7. int main()
  8. {
  9. ios::sync_with_stdio(false);
  10. cin >> n;
  11. for (int i = 1; i <= n; i++)
  12. cin >> a[i];
  13. for (int i = 1; i <= n; i++)
  14. {
  15. f[i] = 1; // 防止前面没有小于a[i]
  16. for (int j = 1; j <= i; j++)
  17. if (a[j] < a[i]) // 只要有小于a[i]的就更新, 一定是需要比较的
  18. f[i] = max(f[i], f[j] + 1);
  19. }
  20. int res = 0;
  21. for (int i = 1; i <= n; i++)
  22. res = max(res, f[i]);
  23. cout << res << endl;
  24. return 0;
  25. }

三:最长公共子序列长度

  • https://www.acwing.com/solution/content/8111/解释的很好
  • y总解释的很详细,中间01的时候,代表b[j]一定是第二个字符串,但是01是这个意思,这句话也就体会出来f[i - 1][j]范围是更大的。

image.png

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. const int N = 1010;
  5. char a[N], b[N];
  6. int f[N][N];
  7. int main()
  8. {
  9. ios::sync_with_stdio(false);
  10. int n, m;
  11. cin >> n >> m;
  12. cin >> a + 1 >> b + 1;
  13. for (int i = 1; i <= n; i++)
  14. for (int j = 1; j <= m; j++)
  15. {
  16. f[i][j] = max(f[i - 1][j], f[i][j - 1]);
  17. if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
  18. }
  19. cout << f[n][m] << endl;
  20. return 0;
  21. }