题目
LeetCode 2218. 从栈中取出K个硬币的最大面值和(hard)
一张桌子上总共有 n
个硬币 栈 。每个栈有 正整数 个带面值的硬币。
每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里。
给你一个列表 piles
,其中 piles[i
] 是一个整数数组,分别表示第 i
个栈里 从顶到底 的硬币面值。同时给你一个正整数 k
,请你返回在 恰好 进行 k
次操作的前提下,你钱包里硬币面值之和 最大为多少 。
示例 1:
输入:piles = [[1,100,3],[7,8,9]], k = 2
输出:101
解释:
上图展示了几种选择 k 个硬币的不同方法。
我们可以得到的最大面值为 101 。
示例 2:
输入:piles = [[100],[100],[100],[100],[100],[100],[1,1,1,1,1,1,700]], k = 7
输出:706
解释:
如果我们所有硬币都从最后一个栈中取,可以得到最大面值和。
提示:
n == piles.length
1 <= n <= 1000
1 <= piles[i][j] <= 105
1 <= k <= sum(piles[i].length) <= 2000
解释
① 把背包容量看成k
,每个栈代表一种物品,所需容量为从此栈中取出的数的个数,跟一般的0-1背包不同的是,这里每个物品所占的容量是会变化的,取决于从这个栈里取多少数出来
② 对于第i
个物品,即第i
个栈,遍历其可能拿出的数所有情况。j
代表到第i
个栈为止一共往背包里装了多少个数。l
代表从第i
个栈里取出了多少个数。即有转移方程dp[i][j] = max(dp[i][j], dp[i - 1][j - l] + piles[i - 1][l - 1])
,其中piles[i - 1][l - 1]
已经是前缀和
③ 这里需要注意for循环的范围:
i
的范围为第1个栈到第n个栈,所以转化为下标、取对应的前缀和piles[i - 1][l - 1]
时,i
需要减一j
代表到第i
个栈为止一共往背包里装了多少个数,最大不能超过k
,但同时也不能超过从第1
个栈到第i
个栈为止所有栈中的数的个数之和,因为显然不能将所有栈中的数字都取出来后再继续取。因此需要一个sum
来记录。还有,从当前栈中不取任何数的情况单独处理,不放进for循环中,因此j >= 1
l
代表从第i
个栈里取出了多少个数,不能超过当前栈的数的总数,也不能超过当前假定的、到这个栈为止一共往背包里装了多少个数,即j
代码(C++实现)
class Solution {
public:
int maxValueOfCoins(vector<vector<int>>& piles, int k) {
int n = piles.size();
vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0));
int ret = 0;
int sum = 0;
for (int i = 1; i <= n; ++i) {
sum += piles[i - 1].size();
for (int j = 1; j < piles[i - 1].size(); ++j)
piles[i - 1][j] += piles[i - 1][j - 1];
for (int j = 1; j <= k && j <= sum; ++j) {
dp[i][j] = dp[i - 1][j];
for (int l = 1; l <= j && l <= piles[i - 1].size(); ++l) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - l] + piles[i - 1][l - 1]);
}
ret = max(ret, dp[i][j]);
}
}
return ret;
}
};
有收获的话,求个赞~ Click Here
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-value-of-k-coins-from-piles