You are given an array piles
where each piles[i]
is a list of positive integers representing coins in the i
-th pile, stacked from top to bottom. You can pick up to k
coins in total from any of the piles, but you can only take coins from the top of each pile (i.e., you may not skip coins within a pile).
Your task is to maximize the total value of coins you pick by choosing at most k
coins in total from all the piles. You cannot pick more than one coin at a time from a pile, and once you skip a coin in a pile, you cannot take any coin below it.
Constraints:
k
.At first glance, this problem might seem to require checking all possible combinations of coins from the piles, but this quickly becomes infeasible as the number of piles and coins grows. The brute-force approach would involve considering every possible way to pick coins from each pile, which grows exponentially.
Instead, we can recognize this as a variant of the classic "Knapsack" problem, where each pile offers a set of choices (take 0, 1, ..., up to the pile's size coins), and we need to maximize the sum subject to the total number of coins not exceeding k
.
This insight leads us to dynamic programming (DP): for each pile, and for each possible number of coins taken so far, we can compute the best total value. We build up solutions for smaller subproblems and use them to solve larger ones efficiently.
We use dynamic programming to solve this problem efficiently. Here's how:
dp[i][j]
represent the maximum value we can achieve using the first i
piles and picking exactly j
coins in total.dp[0][0] = 0
(no piles, no coins, value zero).dp[0][j] = -infinity
for j > 0
(can't pick coins from zero piles).i
, for each possible number of coins j
(from 0 to k
):x
coins from the current pile, where x
ranges from 0 up to min(len(piles[i]), j)
.x
, we calculate the sum of the top x
coins in the pile (using prefix sums for efficiency), and update dp[i+1][j]
as the maximum of its current value and dp[i][j-x] + sum of top x coins
.dp[n][k]
, where n
is the number of piles.This approach ensures we explore all valid ways to pick coins, but only once for each possible total, making it efficient.
Input: piles = [[1,100,3],[7,8,9]]
, k = 2
Let's trace the steps:
dp = [0, 0, 0]
(for 0, 1, 2 coins).Brute-force:
The DP approach is efficient and feasible for the constraints given in the problem.
This problem is a variation of the knapsack problem, where each pile offers a set of choices for how many coins to take from its top. By using dynamic programming, we efficiently explore all valid ways to pick up to k
coins across piles, always taking coins from the top down. The key insight is to use a DP table to store the best total value for each possible number of coins picked so far, updating it as we process each pile. This transforms an exponential brute-force problem into a polynomial-time solution, making it elegant and efficient.
class Solution:
def maxValueOfCoins(self, piles, k):
n = len(piles)
dp = [0] * (k + 1)
for pile in piles:
prefix = [0]
for coin in pile:
prefix.append(prefix[-1] + coin)
for coins in range(k, 0, -1):
for x in range(1, min(len(pile), coins) + 1):
dp[coins] = max(dp[coins], dp[coins - x] + prefix[x])
return dp[k]
class Solution {
public:
int maxValueOfCoins(vector<vector<int>>& piles, int k) {
vector<int> dp(k + 1, 0);
for (auto& pile : piles) {
vector<int> prefix{0};
for (int coin : pile)
prefix.push_back(prefix.back() + coin);
for (int coins = k; coins >= 1; --coins) {
for (int x = 1; x <= min((int)pile.size(), coins); ++x) {
dp[coins] = max(dp[coins], dp[coins - x] + prefix[x]);
}
}
}
return dp[k];
}
};
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int[] dp = new int[k + 1];
for (List<Integer> pile : piles) {
int[] prefix = new int[pile.size() + 1];
for (int i = 0; i < pile.size(); ++i) {
prefix[i + 1] = prefix[i] + pile.get(i);
}
for (int coins = k; coins >= 1; --coins) {
for (int x = 1; x <= Math.min(pile.size(), coins); ++x) {
dp[coins] = Math.max(dp[coins], dp[coins - x] + prefix[x]);
}
}
}
return dp[k];
}
}
var maxValueOfCoins = function(piles, k) {
let dp = new Array(k + 1).fill(0);
for (let pile of piles) {
let prefix = [0];
for (let coin of pile) {
prefix.push(prefix[prefix.length - 1] + coin);
}
for (let coins = k; coins >= 1; --coins) {
for (let x = 1; x <= Math.min(pile.length, coins); ++x) {
dp[coins] = Math.max(dp[coins], dp[coins - x] + prefix[x]);
}
}
}
return dp[k];
};