Maximum Value of K Coins From Piles
Problem
There are n piles of coins on a table. Each pile consists of a positive number of coins of assorted denominations.
In one move, you can choose any coin on top of any pile, remove it, and add it to your wallet.
Given a list piles, where piles[i] is a list of integers denoting the composition of the ith pile from top to bottom, and a positive integer k, return the maximum total value of coins you can have in your wallet if you choose exactly k coins optimally.
Examples
Example 1:

Input:
piles = [[1,100,3],[7,8,9]], k = 2
Output:
101
Explanation:
The above diagram shows the different ways we can choose k coins.
The maximum total we can obtain is 101.
Example 2:
Input:
piles = [[100],[100],[100],[100],[100],[100],[1,1,1,1,1,1,700]], k = 7
Output:
706
Explanation:
The maximum total can be obtained if we choose all coins from the last pile.
Solution
Method 1 – Top Down DP with Memoization
Intuition
We use recursion with memoization to explore all ways to pick coins from piles, keeping track of the best sum for each state. At each pile, we can skip it or take coins from the top, and recursively solve for the remaining piles and coins.
Approach
- Define a recursive function with parameters: current pile index and remaining coins to pick.
- If no coins left or all piles considered, return 0.
- For each pile, try all possible numbers of coins to take (from 0 up to min(k, pile size)).
- For each choice, add the sum of selected coins and recursively solve for the next pile and remaining coins.
- Use memoization to avoid recomputation.
Recurrence Relation
Let f(idx, k) be the maximum value from piles starting at index idx with k coins left to pick.
f(idx, k) = max(
f(idx + 1, k),
max(sum of first j coins from piles[idx] + f(idx + 1, k - j)) for j = 1 to min(k, len(piles[idx]))
)
Base Case
- if
k == 0, i.e. you can't pick any coin then ans = 0 - If
idx == len(piles), you don't have any piles then ans = 0
Code
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, -1));
return helper(piles, dp, 0, k);
}
private:
int helper(const vector<vector<int>>& piles, vector<vector<int>>& dp, int idx, int k) {
if (idx == piles.size() || k == 0) return 0;
if (dp[idx][k] != -1) return dp[idx][k];
int ans = helper(piles, dp, idx + 1, k);
int sum = 0;
for (int i = 0; i < min((int)piles[idx].size(), k); ++i) {
sum += piles[idx][i];
ans = max(ans, sum + helper(piles, dp, idx + 1, k - i - 1));
}
return dp[idx][k] = ans;
}
};
Go
func maxValueOfCoins(piles [][]int, k int) int {
n := len(piles)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, k+1)
for j := range dp[i] {
dp[i][j] = -1
}
}
var helper func(idx, rem int) int
helper = func(idx, rem int) int {
if idx == n || rem == 0 {
return 0
}
if dp[idx][rem] != -1 {
return dp[idx][rem]
}
ans, sum := helper(idx+1, rem), 0
for i := 0; i < len(piles[idx]) && i < rem; i++ {
sum += piles[idx][i]
ans = max(ans, sum+helper(idx+1, rem-i-1))
}
dp[idx][rem] = ans
return ans
}
return helper(0, k)
}
func max(a, b int) int { if a > b { return a }; return b }
Java
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int n = piles.size();
Integer[][] dp = new Integer[n + 1][k + 1];
return helper(piles, dp, 0, k);
}
private int helper(List<List<Integer>> piles, Integer[][] dp, int idx, int k) {
if (idx == piles.size() || k == 0) return 0;
if (dp[idx][k] != null) return dp[idx][k];
int ans = helper(piles, dp, idx + 1, k);
int sum = 0;
for (int i = 0; i < Math.min(piles.get(idx).size(), k); i++) {
sum += piles.get(idx).get(i);
ans = Math.max(ans, sum + helper(piles, dp, idx + 1, k - i - 1));
}
return dp[idx][k] = ans;
}
}
Kotlin
class Solution {
fun maxValueOfCoins(piles: List<List<Int>>, k: Int): Int {
val n = piles.size
val dp = Array(n + 1) { Array(k + 1) { -1 } }
fun helper(idx: Int, rem: Int): Int {
if (idx == n || rem == 0) return 0
if (dp[idx][rem] != -1) return dp[idx][rem]
var ans = helper(idx + 1, rem)
var sum = 0
for (i in 0 until minOf(piles[idx].size, rem)) {
sum += piles[idx][i]
ans = maxOf(ans, sum + helper(idx + 1, rem - i - 1))
}
dp[idx][rem] = ans
return ans
}
return helper(0, k)
}
}
Python
from typing import List
class Solution:
def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
n = len(piles)
dp = [[-1] * (k + 1) for _ in range(n + 1)]
def helper(idx: int, rem: int) -> int:
if idx == n or rem == 0:
return 0
if dp[idx][rem] != -1:
return dp[idx][rem]
ans = helper(idx + 1, rem)
s = 0
for i in range(min(len(piles[idx]), rem)):
s += piles[idx][i]
ans = max(ans, s + helper(idx + 1, rem - i - 1))
dp[idx][rem] = ans
return ans
return helper(0, k)
Rust
impl Solution {
pub fn max_value_of_coins(piles: Vec<Vec<i32>>, k: i32) -> i32 {
let n = piles.len();
let mut dp = vec![vec![-1; (k + 1) as usize]; n + 1];
fn helper(piles: &Vec<Vec<i32>>, dp: &mut Vec<Vec<i32>>, idx: usize, rem: i32) -> i32 {
if idx == piles.len() || rem == 0 { return 0; }
if dp[idx][rem as usize] != -1 { return dp[idx][rem as usize]; }
let mut ans = helper(piles, dp, idx + 1, rem);
let mut sum = 0;
for i in 0..std::cmp::min(piles[idx].len(), rem as usize) {
sum += piles[idx][i];
ans = std::cmp::max(ans, sum + helper(piles, dp, idx + 1, rem - (i as i32) - 1));
}
dp[idx][rem as usize] = ans;
ans
}
helper(&piles, &mut dp, 0, k)
}
}
TypeScript
class Solution {
maxValueOfCoins(piles: number[][], k: number): number {
const n = piles.length;
const dp: number[][] = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1));
function helper(idx: number, rem: number): number {
if (idx === n || rem === 0) return 0;
if (dp[idx][rem] !== -1) return dp[idx][rem];
let ans = helper(idx + 1, rem);
let sum = 0;
for (let i = 0; i < Math.min(piles[idx].length, rem); i++) {
sum += piles[idx][i];
ans = Math.max(ans, sum + helper(idx + 1, rem - i - 1));
}
dp[idx][rem] = ans;
return ans;
}
return helper(0, k);
}
}
Complexity
- ⏰ Time complexity:
O(n * k * m)— n = number of piles, k = coins to pick, m = max coins in a pile. For each pile and coin count, we try all possible coins from the pile. - 🧺 Space complexity:
O(n * k)— DP table stores results for each pile and coin count.
Method 2 – Bottom Up DP
Intuition
We use dynamic programming to build up the answer iteratively. For each pile and each possible number of coins to pick, we compute the best value by considering all ways to take coins from the current pile and combine with previous results.
Approach
- Create a DP table
dp[i][j]whereiis the number of piles considered andjis the number of coins picked. - Initialize
dp[0][*] = 0(no piles, no coins). - For each pile, for each possible number of coins to pick, try all ways to take coins from the current pile (from 0 up to min(j, pile size)).
- Update
dp[i][j]as the maximum of not taking any coins from the current pile or taking some coins and adding their sum to the best result from previous piles.
Recurrence Relation:
Let dp[i][j] be the max value using first i piles and picking j coins.
dp[i][j] = max(
dp[i-1][j],
max(sum of first l coins from piles[i-1] + dp[i-1][j-l]) for l = 1 to min(j, len(piles[i-1]))
)
Base Case:
dp[0][*] = 0(no piles, no coins)dp[*][0] = 0(no coins to pick)
Code
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));
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= k; ++j) {
int sum = 0;
dp[i][j] = dp[i-1][j];
for (int l = 1; l <= min(j, (int)piles[i-1].size()); ++l) {
sum += piles[i-1][l-1];
dp[i][j] = max(dp[i][j], sum + dp[i-1][j-l]);
}
}
}
return dp[n][k];
}
};
Go
func maxValueOfCoins(piles [][]int, k int) int {
n := len(piles)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, k+1)
}
for i := 1; i <= n; i++ {
for j := 0; j <= k; j++ {
sum := 0
dp[i][j] = dp[i-1][j]
for l := 1; l <= j && l <= len(piles[i-1]); l++ {
sum += piles[i-1][l-1]
if val := sum + dp[i-1][j-l]; val > dp[i][j] {
dp[i][j] = val
}
}
}
}
return dp[n][k]
}
Java
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int n = piles.size();
int[][] dp = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= k; j++) {
int sum = 0;
dp[i][j] = dp[i-1][j];
for (int l = 1; l <= Math.min(j, piles.get(i-1).size()); l++) {
sum += piles.get(i-1).get(l-1);
dp[i][j] = Math.max(dp[i][j], sum + dp[i-1][j-l]);
}
}
}
return dp[n][k];
}
}
Kotlin
class Solution {
fun maxValueOfCoins(piles: List<List<Int>>, k: Int): Int {
val n = piles.size
val dp = Array(n + 1) { IntArray(k + 1) }
for (i in 1..n) {
for (j in 0..k) {
var sum = 0
dp[i][j] = dp[i-1][j]
for (l in 1..minOf(j, piles[i-1].size)) {
sum += piles[i-1][l-1]
dp[i][j] = maxOf(dp[i][j], sum + dp[i-1][j-l])
}
}
}
return dp[n][k]
}
}
Python
from typing import List
class Solution:
def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
n = len(piles)
dp = [[0] * (k + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(k + 1):
s = 0
dp[i][j] = dp[i-1][j]
for l in range(1, min(j, len(piles[i-1])) + 1):
s += piles[i-1][l-1]
dp[i][j] = max(dp[i][j], s + dp[i-1][j-l])
return dp[n][k]
Rust
impl Solution {
pub fn max_value_of_coins(piles: Vec<Vec<i32>>, k: i32) -> i32 {
let n = piles.len();
let k = k as usize;
let mut dp = vec![vec![0; k + 1]; n + 1];
for i in 1..=n {
for j in 0..=k {
let mut sum = 0;
dp[i][j] = dp[i-1][j];
for l in 1..=std::cmp::min(j, piles[i-1].len()) {
sum += piles[i-1][l-1];
dp[i][j] = std::cmp::max(dp[i][j], sum + dp[i-1][j-l]);
}
}
}
dp[n][k]
}
}
TypeScript
class Solution {
maxValueOfCoins(piles: number[][], k: number): number {
const n = piles.length;
const dp: number[][] = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 0; j <= k; j++) {
let sum = 0;
dp[i][j] = dp[i-1][j];
for (let l = 1; l <= Math.min(j, piles[i-1].length); l++) {
sum += piles[i-1][l-1];
dp[i][j] = Math.max(dp[i][j], sum + dp[i-1][j-l]);
}
}
}
return dp[n][k];
}
}
Complexity
- ⏰ Time complexity:
O(n * k * m)— n = number of piles, k = coins to pick, m = max coins in a pile. For each pile and coin count, we try all possible coins from the pile. - 🧺 Space complexity:
O(n * k)— DP table stores results for each pile and coin count.