第1章 动态规划
LIS模型
怪盗基德的滑翔翼
求某个点i的max(LIS[0, i], LIS(n, i))
#include <iostream>#include <cstring>using namespace std;const int N = 110;int f[N], a[N];int k, n;int main(){scanf("%d", &k);while (k--){scanf("%d", &n);for (int i=1; i<=n; i++) scanf("%d", &a[i]);int res = 0;for (int i=1; i<=n; i++){f[i] = 1;for (int j=1; j<i; j++)if (a[i] > a[j])f[i] = max(f[i], f[j]+1);res = max(res, f[i]);}memset(f, 0, N);for (int i=n; i>0; i--){f[i] = 1;for (int j=n; j>i; j--)if (a[i] > a[j])f[i] = max(f[i], f[j] + 1);res = max(res, f[i]);}printf("%d\n", res);}return 0;}
登山
由题意是只能是先上升然后下降
求一个两个LIS和-1的最大值

如何确定上升的最高点?
没想到这题真的使用了两个数组
#include <iostream>using namespace std;const int N = 1010;int a[N], f[N], g[N];int main(){int n; cin >> n;for (int i=1; i<=n; i++) cin >> a[i];for (int i=1; i<=n; i++){f[i] = 1;for (int j=1; j<i; j++){if (a[i] > a[j])f[i] = max(f[i], f[j]+1);}}for (int i=n; i>0; i--){g[i] = 1;for (int j=n; j>i; j--){if (a[i] > a[j])g[i] = max(g[i], g[j]+1);}}int res = 0;for (int i=1; i<=n; i++)res = max(res, f[i]+g[i]-1);cout << res;return 0;}
合唱队形
和登山一题一样,只是返回的是n-最大值 求删除最少的人完成队形😀
#include <iostream>#include <climits> // INT_MAXusing namespace std;const int N = 110;int a[N], f[N], g[N];int main(){int n; cin >> n;for (int i=0; i<n; i++) cin >> a[i];for (int i=0; i<n; i++){f[i] = 1;for (int j=0; j<i; j++){if (a[i] > a[j])f[i] = max(f[i], f[j]+1);}}for (int i=n-1; i>=0; i--){g[i] = 1;for (int j=n-1; j>i; j--){if (a[i] > a[j])g[i] = max(g[i], g[j]+1);}}int res = INT_MAX;for (int i=0; i<n; i++)res = min(res, n-(f[i]+g[i]-1));cout << res;return 0;}
友好城市
本题是属于思路上比较难的题目


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

将一个点拓展成多个过程
大盗阿福
不能抢连续点店铺


入口在状态机中也是一个很重要点概念

#include <iostream>#include <algorithm>using namespace std;const int N = 100010;int n;int w[N], f[N][2];int main(){int T;scanf("%d", &T);while (T -- ){scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);for (int i = 1; i <= n; i ++ ){f[i][0] = max(f[i - 1][0], f[i - 1][1]);f[i][1] = f[i - 1][0] + w[i];}printf("%d\n", max(f[n][0], f[n][1]));}return 0;}
股票买卖IV


由于本题有出手次数的限制,状态表示的时候要引入一维来表示
#include <iostream>#include <cstring>#include <climits>using namespace std;const int N = 100010, K = 110;int f[N][K][2], w[N];int main(){int n, k;cin >> n >> k;for (int i=1; i<=n; i++) cin >> w[i];/*条件初始化*/memset(f, -0x3f, sizeof f);// 出手0次,获利为0for (int i=0; i<=n; i++) f[i][0][0] = 0;for (int i=1; i<=n; i++){for (int j=1; j<=k; j++){f[i][j][0] = max(f[i-1][j][0], f[i-1][j][1] + w[i]);f[i][j][1] = max(f[i-1][j-1][0]-w[i], f[i-1][j][1]);}}int res = 0;for (int i=0; i<=k; i++)res = max(res, f[n][i][0]); // 作为一次完整的交易,最有一次肯定是手中无货的cout << res;return 0;}
1058. 股票买卖 V

初始化一定要,否者这里是通不过的
#include <iostream>#include <climits>using namespace std;const int N = 100010;int w[N], f[N][3];int main(){int n; cin >> n;for (int i=1; i<=n; i++)cin >> w[i];// 记住初始化f[0][0] = f[0][1] = INT_MIN;for (int i=1; i<=n; i++){f[i][0] = max(f[i-1][0], f[i-1][2]-w[i]);f[i][1] = f[i-1][0] + w[i];f[i][2] = max(f[i-1][2], f[i-1][1]);}cout << max(f[n][1], f[n][2]);return 0;}
背包问题
487. 金明的预算方案
第4章 高级数据结构
4.1 并查集
- 合并两个集合
- 查询某个元素的祖宗节点
**维护的两种形式**
- 记录每个集合大小 (绑定到根节点)
- 每个点到根节点的距离 (绑定到每个元素上)
食物链 ===》 拓展域
拓展域的并查集有一种枚举的思想在
1250. 格子游戏

第一次出现环,计算画的次数
本题算简单,需要将二维代码做一个简答的映射
#include <iostream>using namespace std;const int N = 40010;int p[N], m, n;int get(int x, int y){return x*n + y;}int find(int x){if (p[x] != x) p[x] = find(p[x]);return p[x];}int main(){cin >> n >> m;for (int i=1; i<n*n; i++) p[i] = i;int res = 0;for (int i=1; i<=m; i++){int x, y;char d;cin >> x >> y >> d;x--, y--;int a = get(x, y);int b;if (d == 'D') b = get(x+1, y); // downelse b = get(x, y+1); // rightint pa = find(a), pb = find(b);if (pa == pb){res = i;break;}p[pa] = pb; // 合并}if (!res) puts("draw");else cout << res << endl;return 0;}
252. 搭配购买
01背包问题 虽然看着像是一个由依赖的背包问题,实际上是可以将所有的连通块找出来,然后将每个连通块看作一个问题,转化为一个01背包问题
#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 10010;int n, m, vol;int v[N], w[N];int p[N];int f[N];int find(int x){if (p[x] != x) p[x] = find(p[x]);return p[x];}int main(){cin >> n >> m >> vol;for (int i = 1; i <= n; i ++ ) p[i] = i;for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];while (m -- ){int a, b;cin >> a >> b;int pa = find(a), pb = find(b);if (pa != pb){v[pb] += v[pa];w[pb] += w[pa];p[pa] = pb;}}// 01背包for (int i = 1; i <= n; i ++ )if (p[i] == i)for (int j = vol; j >= v[i]; j -- )f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[vol] << endl;return 0;}
4.2 树状数组

