dp入门

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

dp[j] = max(dp[j], dp[j - c[i]] + v[i]);
}
}
i\j
j=0
i=0
0
j=1 j=2 j=3 j=4 j=5 j=6
0
00000i=10
3
6
9
12 15 18
i=2
0
3
6
10
13 16 20
17
多重背包
有n种物品,每种最多可以取x[i]个,每个物品有大小和价值,问总大小不超过m的情况下能取到最大的 价值是多少 代码如下: for (int i = 1; i <= n; ++i) {
} } 注意此时j一定是倒序产生
16
完全背包
有n种物品,每种最多可以取无数个,每个物品有大小和价值,问总大小不超过m的情况下能取到最大 的价值是多少
01背包中第二维改成正向即可:
for (int i = 1; i <= n; ++i) {
for (int j = c[i]; j <= m; ++j) {
}
10
数字三角形
线性DP解法 for(int i=m-1;i>0;i--) {
for(int j=0;j<=i;j++) { a[i-1][j]+=max(a[i][j],a[i][j+1]);
} }
11
数字三角形
暴力递归搜索解法: int dfs(int i, int j) {
if (i == n) return a[i][j]; return max(dfs(i+1,j), dfs(i+1,j+1)) + a[i][j]; }
18
背包记录路径问题
滚动优化虽然很好用而且很好理解,但是如果提到记录路径,滚动优化派不上用场了。 有一类dp问题: 比如求最长递增子序列、01背包求取哪些值可以完全填充背包等等 称作需要记录路径的dp,此类问题中不能使用滚动优化 有两种典型的方式去解决此类问题: 1、不使用滚动优化并将dp数组的递推关系找出来 2、另外记录一个数组表示当前的状态是由哪个状态转移来(此方法占用空间更小)
}
30
区间DP
区间dp与线性dp唯一不同就是状态转移的顺序 区间dp的顺序是从区间长度小的往区间大的转移(或大到小) 经典写法: for (int len = 1; len < n; ++i) {
for (int i = 1; i + len <= n; ++i) { int j = i + len; for (int k = i; k <= j; ++k) { dp[i][j] = dp[i][k] ... dp[k][j]; }
res = max(res, dp[i]); }
9
最长公共子序列(LCS)
for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { if (a[i] == b[j]) dp[i][j] = dp[i - 1][j - 1] + 1; else dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]); }
Dynamic Programming入门
DP介绍
状态、阶段、决策、最优子结构、状态转移方程
DP简介
DP本意为动态规划,是求解决策过程最优化的数学方法 该思想在多步骤的决策中有很重要的地位 DP基本包括线性、区间、树形、环形等 所有的DP最重要的两点是状态和状态转移方程 所谓状态就是你可以用多个值去表示某时刻 比如斐波那契数列 Fib[i+j] = fib[i] + fib[j] 其中”i+j”, “I”, “j”就是状态,这个示例中只用了一个值去表示状态,而他们的值就是当前状态的解, 注意对于确定的状态有确定的解
写出来大概是 for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= c[i]; ++j) dp[i][j] = dp[i - 1][j]; for (int j = c[i]; j <= m; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - c[i]] + v[i]); } }
} }
31
区间DP
区间dp的边界值一般是由长度为1的区间确定的,可以注意一下
8
最长上升子序列(LIS)
for (int i = 1; i <= n; ++i) { dp[i] = 1; for (int j = 1; j < i; ++j) { if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1); }
} int res = 0; for (int i = 1; i <= n; ++i) {
此题为环形,处理方法为将序列翻倍,其中从i到j(i>j)的这段状态就由i到j+n这段状态代替
关键代码:
for (int len = 2; len < n; ++len) { for (int i = 1; i + len <= 2 * n; ++i) { int j = i + len; dp[i][j].first = INF; for (int k = i; k < j; ++k) { dp[i][j].first = min(dp[i][j].first, dp[i][k].first + dp[k + 1][j].first + sum(i, j)); dp[i][j].second = max(dp[i][j].second, dp[i][k].second + dp[k + 1][j].second + sum(i, j)); } }
典型状压dp特征: 给定的数字范围小、与集合相关概念 有关 没有顺序区分所以暴力递推出所有集 合即可,比较入门。
26
状压DP
dp[0] = 1; for (int i = 1; i <= n; ++i) {
for (int j = 0; j < (1<<k); ++j) { if (dp[j] == 0) continue; int stat = j; for (int gg = 0; gg < k; ++gg) { if (a[i][gg]) stat |= (1<<gg); } dp[stat] = 1;
} } if (dp[m] != m) printf("No Solution\n"); else {
int tmp = m, now = n; while (tmp) {
if (path[now][tmp]) { res.push_back(a[now]); tmp -= a[now];
} now--; } for (int i = 0; i < res.size(); ++i) printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' '); }
23
状压DP
善用位运算 求某状态中第i个物品是否取了: (Now>>(i-1))&1 往当前状态中加入第i个物品: Now |= 1<<(i-1) 等
24
状压DP
状压的局限性: 状态数可以多但是物品不能超过19个 状压难点: 也是线性DP,因为第i个状态中的所有子状态一定是在0~i-1中
25
状压DP
19
01背包
大意:01背包、 求能否正好取 到m,输出方 案,要求字典 序最小。
20
01背包
sort(a + 1, a + 1 + n, [](int x, int y) { return x > y;
}); for (int i = 1; i <= n; ++i) {
for (int j = m; j - a[i] >= 0; --j) { if (dp[j] <= dp[j - a[i]] + a[i]) { dp[j] = dp[j - a[i]] + a[i]; path[i][j] = 1; }
dp[now][j] = dp[!now][j] ... } }
15
01背包
在经典的01背包问题中可以利用滚动优化将内存占用降到一维 注意:由于只使用一维要格外注意求解顺序问题,因为两个状态使用的都是同一个数组去记录 代码如下: for (int i = 1; i <= n; ++i) {
for (int j = m; j >= c[i]; --j) { dp[j] = max(dp[j], dp[j - c[i]] + v[i]);
3
DP简介
动态规划还有另一个名字叫记忆化搜索,dfs是动归中很重要的一部分,常用于状态转移不容易线性 确定的时候。
所谓状态转移 例如fib[i+j]=fib[i]+fib[j]这个方程就称作状态转移方程 在fib[i]和fib[j]都已知的情况下求出fib[i+j]就称作状态转移 它的记忆化搜索写法 Void dfs(int now) {
} } int res = 0; for (int i = 1; i < (1<<k); ++i) {
if (dp[i]) res++; }
27
区间DP
一段一段的进行dp
区间DP
区间dp是区分与线性dp来说的 究极经典例题: 石子合并
29
区间DP
状态转移方程: Dp[i][j] = min(dp[i][k] + dp[k+1][j] + sum(I,j)) 其中k在ij之间sum表示从i加到j
21
状压DP
状态压缩、01的世界
状压DP
在状态的表示中,偶尔会发现有些很难表达的状态,比如n个物品取了哪些。 状态压缩:1表示当前取了它,0表示没取 设n为6 比如全都不取就是000000 只取了第2个就是000010 取了2和4就是001010 二进制表示是唯一的,而也可以只使用一个数去表示状态,这就是状态压缩的关键
14
01背包
滚动优化: 当状态中的某一维的当前状态只和前x个状态相关,可以将这一维大小优化到x+1甚至x 典型的滚动优化示例:
int now = 1; int dp[2][maxn]; for (int i = 1; i <= n; ++i) {
now = !now; dp[now].clear(); for (int j = 1; j <= n; ++j) {
记忆化搜索解法: int dfs(int i, int j) {
if (i == n) return dp[i][j] = a[i][j]; if (dp[i][j] != -1) return dp[i][j]; return dp[i][j] = max(dfs(i+1, j), dfs(i+1,j+1)) + a[i][j]; }
for (int k = 0; k < x[i]; ++k) { for (int j = c[i]; j <= m; ++j) { dp[j] = max(dp[j], dp[j - c[i]] + v[i]); }
} }
最容易理解的方式就是每个重复x[i]次,具体有其他优化方法可以自行看博客 。
5
DP简介
DP过程: 1、确定状态 2、确定状态转移方程 3、确定初始条件 4、按正确顺序求解 5、输出要求的状态
6
线性DP
具有线性“阶段”划分的动态规划
线性DP
还是一样、斐波那契数列就是一个典型的线性DP的例子 它有着线性的结构,要求解状态较大的值需要先求出状态较小的值 线性经典问题有: 最长上升子序列(LIS) 最长公共子序列(LCS) 数字三角形 ……
12
背包DP
推荐文章,背包九讲
01背包
有n件物品,每个物品有大小和价值,问总大小不超过m的情况下能取到最大的价值是多少
if (背包体积j小于物品i的体积) f[i][j] = f[i-1][j] //背包装不下第i个物体,目前只能靠前i-1个物体装包 else f[i][j] = max(f[i-1][j], f[i-1][j-Vi] + Wi)
if (fib[now]) return fib[now]; return fib[now] = dfs(now-1, now-2); }
4
DP简介
后效性:已经求解过的子问题不会受到后续阶段的影响称为无后效性 一般来说DP很好处理无后效性问题,当然有特殊的方法去除后效性也可以用DP来求解 最优子结构:下一阶段的最优解能由前面某些阶段求出,称为具有最优子结构性质 “状态”、“阶段”、“决策”是构成动态规划算法的三要素,“子问题重叠性”、“无后效性”、 “最优子结构”是使用动态规划求解的基本条件。
相关文档
最新文档