2218. 从栈中取出 K 个硬币的最大面值和¶
难度:困难
题目¶
一张桌子上总共有 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] <= 10^5
1 <= k <= sum(piles[i].length) <= 2000
题解¶
从前i + 1
个栈中取出j
个元素的最大面值和可以分解为从第i + 1
个栈中取出k
个硬币,剩余j - k
个硬币从前i
个栈中按照最优(最大)的方式取出。取出的方式有如下可能:
- 从第
i + 1
个栈中取出0
个硬币,从剩余i
个栈中取出j
个硬币 - 从第
i + 1
个栈中取出1
个硬币,从剩余i
个栈中取出j - 1
个硬币 - ...
- 从第
i + 1
个栈中取出j
个硬币,从剩余i
个栈中取出0
个硬币
遍历每个栈,使用一个表dp[j]
存储在第i
次遍历中,从前i
个列表中取出恰好j
个硬币的最大面值和。那么,根据取出方式,下一轮次的dp[j]
为如下所述各种取出方式的最大值。
- 从第
i + 1
个栈中取出0
个硬币,从剩余i
个栈中取出j
个硬币,最大面值为dp[j]
- 从第
i + 1
个栈中取出1
个硬币,从剩余i
个栈中取出j - 1
个硬币,最大面值为dp[j - 1] + sum(piles[i][:1])
- ...
- 从第
i + 1
个栈中取出j
个硬币,从剩余i
个栈中取出0
个硬币,最大面值为dp[0] + sum(piles[i][:j])
在初始状态时,dp
的值为第一个栈的面值前缀和,dp[0] = 0
对应不取出硬币的情况。每轮迭代更新dp
,最后一轮更新后,dp[k]
即为所求。
class Solution:
def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
dp = [0, *itertools.accumulate(piles[0])][:k + 1]
num_piles, step = len(piles), 1
while step < num_piles:
dp_length, pile_length, new_choices = len(dp), len(piles[step]), []
cum = [0, *itertools.accumulate(piles[step])]
for total in range(k + 1):
max_value = 0
for right in range(min(pile_length, total), -1, -1):
left = total - right
if left >= dp_length:
break
max_value = max(max_value, dp[left] + cum[right])
new_choices.append(max_value)
dp = new_choices[:k + 1]
step += 1
return dp[-1]