【完全背包】LeetCode 1449. 数位成本和为目标值的最大数字
题目
https://leetcode.cn/problems/form-largest-integer-with-digits-that-add-up-to-target/description/
题解
因为每种面额的硬币能选的数量是没有限制的,因此这满足完全背包模型。
状态定义:定义一个大小为 \(target\) 的数组 \(dp\),每个节点用一个结构体维护三个整数 \(x, preffixIndex, length\)。\(dp[i]\) 代表当成本和为 \(i\) 时,所选的最后一个硬币的面值 \(x\),上一个选中的下标位置 \(preffixIndex\) 以及包含当前节点的长度为 \(length\);
状态转移方程:
\[dp[j] = \begin{cases}
\{ i + 1, j - cost[i], dp[j-cost[i]].length+1 \}, & j-cost[i] \geq 0, dp[j-cost[i]].length+1>=dp[j].length \text{ 并且 } dp[j - cost[i]].x != 0 \\
dp[j], & others
\end{cases}
\]
参考代码
struct node {
int x;// 所选的硬币面值
int preffixIndex;// 上一个选中的下标位置,$0$ 代表没有上一个位置
int length;// 包含当前节点的长度, $0$ 代表没有被选中的节点
};
class Solution {
public:
string largestNumber(vector<int>& cost, int target) {
string ans;
vector<node> dp(target + 1);
dp[0] = {1, 0, 0};// 将 dp[0].x 初始化为 1,让其它下标可以从 dp[0] 转移过去
for (int i = 0; i < 9; ++ i) {// 一定要从小到大选,这样当遇到长度相同的情况时,必定可以覆盖,因为多选了一个面值较大的硬币
for (int j = cost[i]; j <= target; ++ j) {// 执行状态转移
int len = dp[j - cost[i]].length + 1;// 计算包含当前节点在内总共的长度
if (len >= dp[j].length && dp[j - cost[i]].x != 0) {// 选中一个面值为 $(i + 1)$ 的硬币,并且从 $dp[j-cost[i]]$ 转移过来
dp[j] = {i + 1, j - cost[i], len};// 更新为更优的答案
}
}
}
for (int i = target; dp[i].length > 0; i = dp[i].preffixIndex) {// 逆序存入答案
ans.push_back(dp[i].x + '0');
}
if (ans.empty()) ans = "0";// 不存在合法方案,根据题目要求返回 "0"
return ans;
}
};
浙公网安备 33010602011771号