DP 重修笔记
DP 重修笔记
背包DP
其实大多的背包问题都是转换成 背包再优化复杂度的。
- 背包问题
就是物品只有选和不选两种选择,对应着 关系,所以叫 背包。
我们设 表示前 个物品,容量为 的背包最多能装下的价值。
是由 转移来的,对于第 个物品,只有选和不选两种可能,不选的话,就等于 , 选的话,就是由 转移来的。
这样的话空间和时间复杂度为 。
但是我们仔细观察,第 次抉择只与 次抉择有关系,所以我们可以将第一位滚动掉。
但是我们要保证你用 来更新的时候,存的是 时更新的,所以我们要保证在枚举他的时候, 还未被更新过,所以我们要倒叙枚举他,来保证 是 的。
for(int i = 1; i <= n; i++) for(int j = V; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]);
- 多维费用背包
就套循环就行了,拿二维举个例子。
for(int i = 1; i <= n; i++) { for(int j = V; j >= v[i]; j--) { for(int k = M; k >= m[i]; k--) { f[j][k] = max(f[j][k], f[j - v[i]][k - m[i]] + w[i]); } } }
- 完全背包问题
当你改变了枚举顺序, 在 的情况下 会被 多次更新,这相当于将物品 多次放入背包。
for(int i = 1; i <= n; i++) for(int j = v[i]; j <= V; j++) f[j] = max(f[j], f[j - v[i]] + w[i]);
- 多重背包问题:
就是每个物品有 种。
我们可以把他看成 个不同的物品,每个物品可以选一遍。
于是就转换成了 背包问题。
复杂度
这样的复杂度还不是太优。我们考虑优化分组的过程。我们知道一个数一定可以由 的幂次表示。
于是我们用二进制来优化。
int n = read(), V = read(); for(int i = 1 ; i <= n; i++) { int v = read(), w = read(), k = read(); int c = 1; while(k - c >= 0) { k -= c; a[++idx].v = c * v; a[idx].w = c * w; } a[++idx].v = v * k; a[idx].w = w * k; }
这样分完后再来做 背包就行。
复杂度为 。
线性DP
- 最长公共子序列 和 最大上升子序列
其实是一类问题,这里只讲 的做法。
我们设 表示长度为 的最长上升子序列的末端元素大小。
显然 。
如果 显然可以直接接在后面 。
否则就找一个位置将他替换掉,找到第一个比他大的数去调换掉,这样肯定更优。
int n, a[MAXN], b[MAXN]; int rk[MAXN], dp[MAXN], len; signed main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(), rk[a[i]] = i; for(int i = 1; i <= n; i++) b[i] = read(), b[i] = rk[b[i]]; dp[1] = b[1], len = 1; for(int i = 2; i <= n; i++) { if(b[i] >= dp[len]) dp[++len] = b[i]; else dp[upper_bound(dp + 1, dp + len + 1, b[i]) - dp] = b[i]; } return print(len), 0; }
LCS 很简单, LIS 也很简单,那 LCIS 呢?
也很简单。
int n, m, a[N], b[N]; int f[1000][1000], pre[1000][1000]; int ANS[N], cnt = 0; signed main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(); m = read(); for(int i = 1; i <= m; i++) b[i] = read(); a[0] = b[0] = -INF; for(int i = 1; i <= n; i++) { int Max = 0, pos = 0; for(int j = 1; j <= m; j++) { f[i][j] = f[i - 1][j]; pre[i][j] = j; if(a[i] == b[j]) { if(f[i][j] < Max + 1) f[i][j] = Max + 1, pre[i][j] = pos; } if(a[i] > b[j]) { if(f[i - 1][j] > Max) Max = f[i - 1][j], pos = j; } } } int ans = 0, wz = 0; for(int i = 1; i <= m; i++) { if(f[n][i] > ans) ans = f[n][i], wz = i; } printf("%d\n", ans); int i = n, j = wz; while(i >= 1 && j >= 1) { // cout << i << " " << j << "\n"; if(pre[i][j] != j) ANS[++cnt] = b[j]; j = pre[i][j], i--; } for(int i = cnt; i >= 1; i--) printf("%d ", ANS[i]); return 0; }
区间 dp
本文作者:TLE_Automation
本文链接:https://www.cnblogs.com/tttttttle/p/16436144.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!