动态规划
int 4字节 1MB = 2e5 int
1.状态定义: 经验积累多做题
2.状态转移: 一般以最后一次不同为划分分类转移
3.转移顺序: 需要保证所要计算的状态需要的状态已经被算出 背包等优化另说
4.初始化: 根据定义初始化
线性动态规划
数字三角形模型
- 数字三角形 从上走到下,max or min
- 状态定义: dp[i][j] 从(1, 1)走到(i, j)的所有方案中的 max or min
- 状态转移: 从左上走下来 or 右上走下来
- 摘花生 左上到右下 n*m 只能向下或向右 步数(n * m - 2)
- 几乎同数字三角形
- 传纸条 两次摘花生 两次经过同一点的话只加一次权
两次一起走 小分析 若步数相同 则 x1 + y1 = x2 + y2 则设 k = x1 + y1 = x2 + y2 这样只有三维即可
- 状态定义: dp[k][i1][i2] 则 j1 = k - i1; j2 = k - i2; 分别从(1, 1)走到(i1, j1) (i2, j2)时的所有方案中的max or min
- 状态转移: 若 i1 == i2 则在同一格 只将当前格子权值加一次 考虑分四类 两条路线来当前状态的方式进行转移
最长上升子序列模型(LIS)
-
最长上升/最长下降 和最长上升加下降
-
Dilworth定理 一个序列要最少分割为多少个不上升子序列的个数等于该序列最长上升子序列的长度
-
友好城市: 每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故,能批准的最多申请数。
- 转化: 每对友好城市绑定,如果按照一边的城市标号排序,那么所能选的方案 在另一侧城市标号一定也是递增的否则将会交叉
- a < b && A > B 就会交叉 所以只需求另一侧的LIS
4.导弹防御系统 加强版导弹拦截 拦截可递增可递降
- dfs试放到递增还是递降 贪心思路与导弹拦截一致 迭代加深或者记录答案剪枝
- 最长公共上升子序列 LIS与LCS结合非常有趣...
- 状态定义: dp[i][j] LCS(A前i个元素 B前j个元素) LIS(以b[j]结尾) 的LICS
- 转移: 1.若a[i]≠b[j] -> dp[i-1][j]
- 2.若a[i] == b[j] 则需根据序列的倒数第二个元素转移 枚举b[k] (0<=k<=j-1)作为倒数第二个 if(b[j] > b[k]) dp[i][j] = max(dp[i][j], dp[i-1][k] + 1)
代码
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
const int N = 3030;
int a[N], b[N];
int f[N][N];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
int ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
f[i][j] = f[i - 1][j];
if (a[i] == b[j])
{
for (int k = 0; k <= j - 1; k++)
{
if (b[k] < b[j])
f[i][j] = max(f[i][j], f[i - 1][k] + 1);
}
}
ans = max(ans, f[i][j]);
}
}
printf("%d", ans);
return 0;
}
复杂度O(n^3)
考虑优化: 把枚举的步骤在算之前j时一起算了
for (int i = 1; i <= n; i++)
{
int mx = 0;
for (int j = 1; j <= n; j++)
{
f[i][j] = f[i - 1][j];
if (a[i] == b[j]) f[i][j] = max(f[i][j], mx + 1);
if (a[i] > b[j]) mx = max(f[i - 1][j], mx);
ans = max(ans, f[i][j]);
}
}
背包问题!!(组合DP)
- 01背包 n个物品,每个物品有体积和价值 求体积不超过V的最大价值 小练习
- f[i][j]: 从前i个物品中选体积不超过j的MAX(value)
- 转移: 不选第i个物品f[i-1][j] 选第i个物品f[i-1][j-v[i]] + w[i]
- 复杂度 O(nV)
- 完全背包 不同于01 每种物品有无数个
- 状态计算: 枚举第i个物品选几个(0~s) s = j/v[i]
- 优化:
f[i][j] = max(f[i - 1][j], f[i - 1][j - v] + w, f[i - 1][j -2v] + 2w,... ..f[i - 1][j - sv] + sw)
f[i][j-v] = max(f[i - 1][j - v], f[i - 1][j - 2v] + w, f[i - 1][j - 3v] + 2w,.....f[i - 1][j - sv] + (s-1)w)
So: f[i][j] = max(f[i - 1][j], f[i][j - v]+w) 与01非常相似
- 多重背包 每种物品有有限个,上线不一样
朴素 O(nms) 枚举顺序:物品,体积,决策
二进制优化 O(nmlogs)
单调队列优化 O(n*m) ....下次写 难 - 二维费用背包
- 体积至少是j 潜水员 体积恰好是j v>=0
- 背包问题求具体方案 01方案 还有一种记录自己是从哪来的
- 分组背包具体方案 机器分配
- 金明 n个数选或不选 二进制枚举 0~2^n 每个数代表一种选法
- 有依赖的背包 挺难 树形DP+分组背包 例题
- 背包问题求方案数 开一个数组g[i] 表示到达i的方案数即可
- 能量石 先贪心再DP
状态机(另类状态表示)
将单个混沌状态 拆分成多个清晰状态以便转移
学完KMP,AC自动机后继续后两题
状态压缩DP 扩展版状态机
一.棋盘式(基于连通性的DP)
- 蒙德里安的梦想
-
状态表示: f[i][j]已经将前i-1列摆好,并且从i-1列伸向第i列的状态为j的合法方案数
-
转移 f[i][j] += f[i-1][k] //k的约束为 k和j在一起是合法状态 可以考虑预处理
(1) k&j==0 (2) k|j不能出现连续奇数个0
- [SCOI2005] 互不侵犯 田字形不能放
-
状态表示: f[i][j][s]前i列已经摆好,用了j个国王,第i列状态为s 的方案数
-
转移 f[i][j][s] += f[i-1][j-count(s)][k] //k的约束为 k和j在一起是合法状态 可以考虑预处理
(1) k&j==0 (2) k|j不能有连续两个1 (3) k or j 不能有连续两个1
- [USACO06NOV] Corn Fields G 十字形不能放
还有被破坏的田地不能放不枚举即可 当作不合法状态
-
状态表示: f[i][s]前i列已经摆好,第i列状态为s 的方案数
-
转移 f[i][s] += f[i-1][k] //k的约束为 k和j在一起是合法状态 可以考虑预处理
(1) k&j==0 (2) k or j 不能有连续两个1 (3)k or j 不放不育土地
y总把不育土地看作已经种上的
- [NOI2001] 炮兵阵地 扩展一维
- 状态表示: f[i][j][k]前i列已经摆好,第i列状态为j 第i-1列状态为k 的最大值
转移限制
(1) k&j || k&u || j&u == 0
(2) k,j,u合法 1.不能在三格内放两个 不能放高原
二.集合式
- 最短Hamilton路径
-
从1不重不漏地走到N 的最小代价
-
状态表示: f[i][j] 目前走到i并且已经走过了j的所有走法中min代价
-
转移: 枚举一个k,(k在j中被选过)f[i][j] = min(f[i][j], f[k][j中除掉k])
需要保证计算每个f[i][j]时f[k][j中除掉k]已经被算好

浙公网安备 33010602011771号