第1章 动态规划

LIS模型

怪盗基德的滑翔翼

求某个点i的max(LIS[0, i], LIS(n, i))
image.png

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. const int N = 110;
  5. int f[N], a[N];
  6. int k, n;
  7. int main()
  8. {
  9. scanf("%d", &k);
  10. while (k--)
  11. {
  12. scanf("%d", &n);
  13. for (int i=1; i<=n; i++) scanf("%d", &a[i]);
  14. int res = 0;
  15. for (int i=1; i<=n; i++)
  16. {
  17. f[i] = 1;
  18. for (int j=1; j<i; j++)
  19. if (a[i] > a[j])
  20. f[i] = max(f[i], f[j]+1);
  21. res = max(res, f[i]);
  22. }
  23. memset(f, 0, N);
  24. for (int i=n; i>0; i--)
  25. {
  26. f[i] = 1;
  27. for (int j=n; j>i; j--)
  28. if (a[i] > a[j])
  29. f[i] = max(f[i], f[j] + 1);
  30. res = max(res, f[i]);
  31. }
  32. printf("%d\n", res);
  33. }
  34. return 0;
  35. }

登山

由题意是只能是先上升然后下降
求一个两个LIS和-1的最大值

image.png

如何确定上升的最高点?

没想到这题真的使用了两个数组

  1. #include <iostream>
  2. using namespace std;
  3. const int N = 1010;
  4. int a[N], f[N], g[N];
  5. int main()
  6. {
  7. int n; cin >> n;
  8. for (int i=1; i<=n; i++) cin >> a[i];
  9. for (int i=1; i<=n; i++)
  10. {
  11. f[i] = 1;
  12. for (int j=1; j<i; j++)
  13. {
  14. if (a[i] > a[j])
  15. f[i] = max(f[i], f[j]+1);
  16. }
  17. }
  18. for (int i=n; i>0; i--)
  19. {
  20. g[i] = 1;
  21. for (int j=n; j>i; j--)
  22. {
  23. if (a[i] > a[j])
  24. g[i] = max(g[i], g[j]+1);
  25. }
  26. }
  27. int res = 0;
  28. for (int i=1; i<=n; i++)
  29. res = max(res, f[i]+g[i]-1);
  30. cout << res;
  31. return 0;
  32. }

采用状态机模型的解法

合唱队形

和登山一题一样,只是返回的是n-最大值 求删除最少的人完成队形😀

  1. #include <iostream>
  2. #include <climits> // INT_MAX
  3. using namespace std;
  4. const int N = 110;
  5. int a[N], f[N], g[N];
  6. int main()
  7. {
  8. int n; cin >> n;
  9. for (int i=0; i<n; i++) cin >> a[i];
  10. for (int i=0; i<n; i++)
  11. {
  12. f[i] = 1;
  13. for (int j=0; j<i; j++)
  14. {
  15. if (a[i] > a[j])
  16. f[i] = max(f[i], f[j]+1);
  17. }
  18. }
  19. for (int i=n-1; i>=0; i--)
  20. {
  21. g[i] = 1;
  22. for (int j=n-1; j>i; j--)
  23. {
  24. if (a[i] > a[j])
  25. g[i] = max(g[i], g[j]+1);
  26. }
  27. }
  28. int res = INT_MAX;
  29. for (int i=0; i<n; i++)
  30. res = min(res, n-(f[i]+g[i]-1));
  31. cout << res;
  32. return 0;
  33. }

友好城市

本题是属于思路上比较难的题目

image.png
image.png

只要出席那一种逆序,那么就会出现交叉,那么保证是一种递归的上升即可

状态机模型

image.png
将一个点拓展成多个过程

大盗阿福

不能抢连续点店铺
image.png
image.png
image.png
入口在状态机中也是一个很重要点概念

image.png

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. const int N = 100010;
  5. int n;
  6. int w[N], f[N][2];
  7. int main()
  8. {
  9. int T;
  10. scanf("%d", &T);
  11. while (T -- )
  12. {
  13. scanf("%d", &n);
  14. for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
  15. for (int i = 1; i <= n; i ++ )
  16. {
  17. f[i][0] = max(f[i - 1][0], f[i - 1][1]);
  18. f[i][1] = f[i - 1][0] + w[i];
  19. }
  20. printf("%d\n", max(f[n][0], f[n][1]));
  21. }
  22. return 0;
  23. }

股票买卖IV

image.pngimage.png
由于本题有出手次数的限制,状态表示的时候要引入一维来表示

  1. #include <iostream>
  2. #include <cstring>
  3. #include <climits>
  4. using namespace std;
  5. const int N = 100010, K = 110;
  6. int f[N][K][2], w[N];
  7. int main()
  8. {
  9. int n, k;
  10. cin >> n >> k;
  11. for (int i=1; i<=n; i++) cin >> w[i];
  12. /*条件初始化*/
  13. memset(f, -0x3f, sizeof f);
  14. // 出手0次,获利为0
  15. for (int i=0; i<=n; i++) f[i][0][0] = 0;
  16. for (int i=1; i<=n; i++)
  17. {
  18. for (int j=1; j<=k; j++)
  19. {
  20. f[i][j][0] = max(f[i-1][j][0], f[i-1][j][1] + w[i]);
  21. f[i][j][1] = max(f[i-1][j-1][0]-w[i], f[i-1][j][1]);
  22. }
  23. }
  24. int res = 0;
  25. for (int i=0; i<=k; i++)
  26. res = max(res, f[n][i][0]); // 作为一次完整的交易,最有一次肯定是手中无货的
  27. cout << res;
  28. return 0;
  29. }

1058. 股票买卖 V

image.png
初始化一定要,否者这里是通不过的

  1. #include <iostream>
  2. #include <climits>
  3. using namespace std;
  4. const int N = 100010;
  5. int w[N], f[N][3];
  6. int main()
  7. {
  8. int n; cin >> n;
  9. for (int i=1; i<=n; i++)
  10. cin >> w[i];
  11. // 记住初始化
  12. f[0][0] = f[0][1] = INT_MIN;
  13. for (int i=1; i<=n; i++)
  14. {
  15. f[i][0] = max(f[i-1][0], f[i-1][2]-w[i]);
  16. f[i][1] = f[i-1][0] + w[i];
  17. f[i][2] = max(f[i-1][2], f[i-1][1]);
  18. }
  19. cout << max(f[n][1], f[n][2]);
  20. return 0;
  21. }

背包问题

487. 金明的预算方案

第4章 高级数据结构

4.1 并查集

  1. 合并两个集合
  2. 查询某个元素的祖宗节点

**维护的两种形式**

  1. 记录每个集合大小 (绑定到根节点)
  2. 每个点到根节点的距离 (绑定到每个元素上)

    食物链 ===》 拓展域

拓展域的并查集有一种枚举的思想在

1250. 格子游戏

image.png
第一次出现环,计算画的次数

本题算简单,需要将二维代码做一个简答的映射

  1. #include <iostream>
  2. using namespace std;
  3. const int N = 40010;
  4. int p[N], m, n;
  5. int get(int x, int y)
  6. {
  7. return x*n + y;
  8. }
  9. int find(int x)
  10. {
  11. if (p[x] != x) p[x] = find(p[x]);
  12. return p[x];
  13. }
  14. int main()
  15. {
  16. cin >> n >> m;
  17. for (int i=1; i<n*n; i++) p[i] = i;
  18. int res = 0;
  19. for (int i=1; i<=m; i++)
  20. {
  21. int x, y;
  22. char d;
  23. cin >> x >> y >> d;
  24. x--, y--;
  25. int a = get(x, y);
  26. int b;
  27. if (d == 'D') b = get(x+1, y); // down
  28. else b = get(x, y+1); // right
  29. int pa = find(a), pb = find(b);
  30. if (pa == pb)
  31. {
  32. res = i;
  33. break;
  34. }
  35. p[pa] = pb; // 合并
  36. }
  37. if (!res) puts("draw");
  38. else cout << res << endl;
  39. return 0;
  40. }

252. 搭配购买

01背包问题 虽然看着像是一个由依赖的背包问题,实际上是可以将所有的连通块找出来,然后将每个连通块看作一个问题,转化为一个01背包问题

  1. #include <cstring>
  2. #include <iostream>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 10010;
  6. int n, m, vol;
  7. int v[N], w[N];
  8. int p[N];
  9. int f[N];
  10. int find(int x)
  11. {
  12. if (p[x] != x) p[x] = find(p[x]);
  13. return p[x];
  14. }
  15. int main()
  16. {
  17. cin >> n >> m >> vol;
  18. for (int i = 1; i <= n; i ++ ) p[i] = i;
  19. for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
  20. while (m -- )
  21. {
  22. int a, b;
  23. cin >> a >> b;
  24. int pa = find(a), pb = find(b);
  25. if (pa != pb)
  26. {
  27. v[pb] += v[pa];
  28. w[pb] += w[pa];
  29. p[pa] = pb;
  30. }
  31. }
  32. // 01背包
  33. for (int i = 1; i <= n; i ++ )
  34. if (p[i] == i)
  35. for (int j = vol; j >= v[i]; j -- )
  36. f[j] = max(f[j], f[j - v[i]] + w[i]);
  37. cout << f[vol] << endl;
  38. return 0;
  39. }

4.2 树状数组

image.png

image.png