You are given a rod of integer length n (n >= 0) and an array price where price[i-1] gives the selling price of a piece of length i (1-indexed logic). You may cut the rod into any number of integer-length pieces (or not cut at all). Find the maximum obtainable revenue by cutting and selling the pieces. (Assume unlimited demand; each piece sells for the listed unit price.)
Return the maximum revenue; optionally reconstruct one optimal set of cuts.
This is a classic introductory Dynamic Programming problem (unbounded variant of the knapsack formulation) often appearing right after Fibonacci when teaching overlapping subproblems and optimal substructure.
Core idea: For each length L, consider the first cut at position i (1 ≤ i ≤ L) and combine the price of the left piece with the optimal revenue of the remaining length: best[L] = max_{1 ≤ i ≤ L} ( price[i] + best[L - i] ). This naturally forms overlapping subproblems. Multiple DP formulations (top-down memo, bottom-up 1D, bottom-up 2D) illustrate the same recurrence. Path reconstruction (cut sequence) is done by storing which first cut produced the maximum.
#include<vector>usingnamespace std;
longmaxRodRevenue(const vector<long>& price, int n) {
if (n ==0) return0;
long best =0;
for (int i =1; i <= n; ++i) { // cut first piece length i
best = max(best, price[i-1] + maxRodRevenue(price, n - i));
}
return best;
}
1
2
3
4
5
6
7
8
9
10
11
packagemainfuncmaxRodRevenue(price []int64, nint) int64 {
ifn==0 { return0 }
varbestint64 = 0fori:=1; i<=n; i++ {
v:=price[i-1] +maxRodRevenue(price, n-i)
ifv > best { best = v }
}
returnbest}
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;
classSolutionRec {
publiclongmaxRodRevenue(List<Long> price, int n) {
if (n == 0) return 0L;
long best = 0;
for (int i = 1; i <= n; i++) {
best = Math.max(best, price.get(i-1) + maxRodRevenue(price, n - i));
}
return best;
}
}
1
2
3
4
5
6
7
8
9
from typing import List
defmaxRodRevenue(price: List[int], n: int) -> int:
if n ==0:
return0 best =0for i in range(1, n +1):
best = max(best, price[i-1] + maxRodRevenue(price, n - i))
return best
import java.util.*;
classSolutionTopDownRod {
long[] memo; int[] first;
longdfs(List<Long> price, int L) {
if (L == 0) return 0L;
if (memo[L]!=-1) return memo[L];
long best = 0; int cut = 1;
for (int i = 1; i <= L; i++) {
long val = price.get(i-1) + dfs(price, L - i);
if (val > best) { best = val; cut = i; }
}
first[L]= cut;
return memo[L]= best;
}
public Map<String,Object>maxRodRevenue(List<Long> price, int n) {
memo =newlong[n+1]; first =newint[n+1];
Arrays.fill(memo, -1);
long ans = dfs(price, n);
List<Integer> cuts =new ArrayList<>();
int L = n; while (L > 0) { cuts.add(first[L]); L -= first[L]; }
Map<String,Object> res =new HashMap<>();
res.put("revenue", ans); res.put("cuts", cuts); return res;
}
}
from typing import List, Tuple
from functools import lru_cache
classSolutionTopDownRod:
defmaxRodRevenue(self, price: List[int], n: int) -> Tuple[int, List[int]]:
if n ==0:
return0, []
first: List[int] = [0]*(n+1)
@lru_cache(None)
defdfs(L: int) -> int:
if L ==0:
return0 best =0 cut_choice =1for i in range(1, L+1):
v = price[i-1] + dfs(L-i)
if v > best:
best = v
cut_choice = i
first[L] = cut_choice
return best
ans = dfs(n)
cuts: List[int] = []
L = n
while L >0:
cuts.append(first[L])
L -= first[L]
return ans, cuts
Iteratively build dp[L] for L = 1..n using previously computed revenues for smaller lengths. Keep a firstCut[L] similar to memoized variant but iteratively.
import java.util.*;
classSolutionRodBU1D {
public Map<String,Object>maxRodRevenue(List<Long> price, int n) {
long[] dp =newlong[n+1];
int[] first =newint[n+1];
for (int L = 1; L <= n; L++) {
long best = 0; int cut = 1;
for (int i = 1; i <= L; i++) {
long val = price.get(i-1) + dp[L - i];
if (val > best) { best = val; cut = i; }
}
dp[L]= best; first[L]= cut;
}
List<Integer> cuts =new ArrayList<>();
int L = n; while (L > 0) { cuts.add(first[L]); L -= first[L]; }
Map<String,Object> res =new HashMap<>();
res.put("revenue", dp[n]); res.put("cuts", cuts); return res;
}
}
from typing import List, Tuple
classSolutionRodBU1D:
defmaxRodRevenue(self, price: List[int], n: int) -> Tuple[int, List[int]]:
dp = [0]*(n+1)
first = [0]*(n+1)
for L in range(1, n+1):
best =0 cut_choice =1for i in range(1, L+1):
val = price[i-1] + dp[L-i]
if val > best:
best = val
cut_choice = i
dp[L] = best
first[L] = cut_choice
cuts: List[int] = []
L = n
while L >0:
cuts.append(first[L])
L -= first[L]
return dp[n], cuts
We can view each piece length i as an item usable multiple times (unbounded knapsack). A 2D table dp[i][L] = best revenue using piece lengths up to i to fill total length L. This is pedagogical; 1D optimized version is preferable.
import java.util.*;
classSolutionRodBU2D {
publiclongmaxRodRevenue(List<Long> price, int n) {
long[][] dp =newlong[n+1][n+1];
for (int i = 1; i <= n; i++) {
for (int L = 1; L <= n; L++) {
if (i <= L) {
long take = price.get(i-1) + dp[i][L - i];
long skip = dp[i-1][L];
dp[i][L]= Math.max(take, skip);
} else {
dp[i][L]= dp[i-1][L];
}
}
}
return dp[n][n];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from typing import List
classSolutionRodBU2D:
defmaxRodRevenue(self, price: List[int], n: int) -> int:
dp = [[0]*(n+1) for _ in range(n+1)]
for i in range(1, n+1):
for L in range(1, n+1):
if i <= L:
take = price[i-1] + dp[i][L - i]
skip = dp[i-1][L]
dp[i][L] = max(take, skip)
else:
dp[i][L] = dp[i-1][L]
return dp[n][n]