【每日一题】Problem 189A. Cut Ribbon
解决思路
完全背包问题
用 dp[i][j] 表示到 i 个切分长度,总长度为 j 时,可以裁剪的最大长度
以 5 5 3 2
为例
说明:
- 以 "0" 元素开始的那行,只是为了特殊情况,例如后面防止 \(i-1\) 越界,以及其他可能要设置初始值的时候,需要这行,因此需要保留
- 非 "0" 元素的每行中所包含的 "0", 表示当前情况下的裁剪不满足要求,例如 \([3,4]\),一段为 \(3\),剩下一段为 \(1\),不满足要求
- 非 "0" 元素的每行中所包含的非 "0" 值,代表 \([i,j]\) 下能裁剪的最大段数
- 例如 \([2, 5]\) 可以剪成 \([2, 3] + 1 = 1 + 1 = 2\)
#include <bits/stdc++.h>
int main() {
int n; std::cin >> n;
std::vector<int> len(3, 0);
for (int i = 0; i < 3; ++i) std::cin >> len[i];
std::vector<std::vector<int>> dp(4, std::vector<int>(n + 1, 0));
for (int i = 1; i < 4; ++i) {
for (int j = 1; j <= n; ++j) {
// 当总长度小于目标裁剪长度时,此时无法裁剪出长度 i
// 因此,段数以前一个元素 i-1 在总长度为 j 时可以裁剪的段数为准
if (j < len[i - 1]) dp[i][j] = dp[i - 1][j];
else {
// 需要判断减掉当前长度时,上下的长度能否裁剪出满足要求的长度
// 若不能,则段数以前一个元素 i-1 在总长度为 j 时可以裁剪的段数为准
// 需要注意的是,剩余长度为 dp[i][0] 时是一个特例
if (j != len[i - 1] && dp[i][j - len[i - 1]] == 0) dp[i][j] = dp[i - 1][j];
else dp[i][j] = std::max(dp[i - 1][j], dp[i][j - len[i - 1]] + 1);
}
}
}
std::cout << dp[3][n] << std::endl;
return 0;
}
更好的解
特别地,对于 dp[i][j] = std::max(dp[i - 1][j], dp[i][j - len[i - 1]] + 1)
- 在二维数组中,
dp[i - 1][j]
和dp[i][j]
处于同一列; - 如果把这两个元素放进一维数组
newdp
,那么可以把dp[i-1][j]
看作是更新为dp[i][j]
前的旧值
举个例子:
一维数组newdp
当前值为红框圈出这行的值,当 for 循环进入i == 3
时,以 \([2,3]\) 为例
此时newdp[j] = 1
,即dp[i][j] = 1
,而dp[i-1][j] = 1([3,3])
,对应上一轮的newdp[j] = ${[3,3]}
dp[i][j - len[i - 1]] + 1
即newdp[j-len[i-1]]
,由于 \(j\) 在循环中是升序,因此该值早于newdp[j]
更新
以上,使用滚动数组完成了二维数组向一维数组的转化
- 以下简单地将二维数组替换成一维数组
#include <bits/stdc++.h>
int main() {
int n; std::cin >> n;
std::vector<int> len(3, 0);
for (int i = 0; i < 3; ++i) std::cin >> len[i];
std::vector<int> dp(n + 1, 0);
for (int i = 1; i < 4; ++i) {
for (int j = 1; j <= n; ++j) {
if (j < len[i - 1]) continue;
else {
if (j != len[i - 1] && dp[j - len[i - 1]] == 0) continue;
else dp[j] = std::max(dp[j], dp[j - len[i - 1]] + 1);
}
}
}
std::cout << dp[n] << std::endl;
return 0;
}
- 合并条件分支
#include <bits/stdc++.h>
int main() {
int n;
std::cin >> n;
std::vector<int> len(3, 0);
for (int i = 0; i < 3; ++i) std::cin >> len[i];
std::vector<int> dp(n + 1, 0);
for (int i = 1; i < 4; ++i) {
for (int j = 1; j <= n; ++j) {
if (j >= len[i - 1] && (j == len[i - 1] || dp[j - len[i - 1]] != 0))
dp[j] = std::max(dp[j], dp[j - len[i - 1]] + 1);
}
}
std::cout << dp[n] << std::endl;
return 0;
}
感慨
动态规划真的绕脑子啊,还是回力扣做了好几道动态规划,又学了一下 0-1/完全背包的二维实现、滚动数组实现,积攒了好几天的题,才终于能补上了。。
本文来自博客园,作者:HelloEricy,转载请注明原文链接:https://www.cnblogs.com/HelloEricy/p/17486736.html