Given a rod of length n inches and an array price[], where price[i] denotes the value of a piece of length i (1-based Index). Determine the maximum value obtainable by cutting up the rod and selling the pieces.
Note: The value of n is equal to the size of price array.
Examples:
Input: price[] = [1, 5, 8, 9, 10, 17, 17, 20]
Output: 22
Explanation: The maximum obtainable value is 22 by cutting in two pieces of lengths 2 and 6, i.e., 5 + 17 = 22.Input: price[] = [3, 5, 8, 9, 10, 17, 17, 20]
Output: 24
Explanation: The maximum obtainable value is 24 by cutting the rod into 8 pieces of length 1, i.e, 8 *price[1] = 8 * 3 = 24.Input: price[] = [3]
Output: 3
Explanation: There is only 1 way to pick a piece of length 1.
Table of Content
[Naive Approach] Recursion - O(2 ^ n) Time and O(n) Space
- For a rod of length i, try all possible cuts j (1 ≤ j ≤ i).
- For each cut, add the price of length j with the best profit of remaining rod (i − j).
- Take the maximum profit among all possible cuts.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int cutRodRecur(int i, vector<int> &price)
{
// Base case
if (i == 0)
return 0;
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++)
{
res = max(res, price[j - 1] + cutRodRecur(i - j, price));
}
return res;
}
int cutRod(vector<int> &price)
{
return cutRodRecur(price.size(), price);
}
int main()
{
vector<int> price = {1, 5, 8, 9, 10, 17, 17, 20};
cout << cutRod(price);
return 0;
}
import java.util.Arrays;
public class GFG {
// Recursive function to return the maximum obtainable
// value
static int cutRodRecur(int i, int[] price)
{
// Base case
if (i == 0)
return 0;
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++) {
res = Math.max(res,
price[j - 1]
+ cutRodRecur(i - j, price));
}
return res;
}
static int cutRod(int[] price)
{
return cutRodRecur(price.length, price);
}
public static void main(String[] args)
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
System.out.println(cutRod(price));
}
}
def cutRodRecur(i, price):
# Base case
if i == 0:
return 0
res = 0
# Try every possible first cut
for j in range(1, i + 1):
res = max(res, price[j - 1] + cutRodRecur(i - j, price))
return res
def cutRod(price):
return cutRodRecur(len(price), price)
if __name__ == '__main__':
price = [1, 5, 8, 9, 10, 17, 17, 20]
print(cutRod(price))
using System;
public class GFG {
// Recursive function to return the maximum obtainable
// value
static int cutRodRecur(int i, int[] price)
{
// Base case
if (i == 0)
return 0;
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++) {
res = Math.Max(res,
price[j - 1]
+ cutRodRecur(i - j, price));
}
return res;
}
static int cutRod(int[] price)
{
return cutRodRecur(price.Length, price);
}
public static void Main()
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
Console.WriteLine(cutRod(price));
}
}
function cutRodRecur(i, price)
{
// Base case
if (i === 0)
return 0;
let res = 0;
// Try every possible first cut
for (let j = 1; j <= i; j++) {
res = Math.max(
res, price[j - 1] + cutRodRecur(i - j, price));
}
return res;
}
function cutRod(price)
{
return cutRodRecur(price.length, price);
}
// Driver Code
const price = [ 1, 5, 8, 9, 10, 17, 17, 20 ];
console.log(cutRod(price));
Output
22
Note: This recursive approach gives TLE because it recalculates the same subproblems multiple times, resulting in exponential time complexity.
[Better Approach] Idea of Unbounded Knapsack - O(n ^ 2) Time and O(n ^ 2) Space
This problem can be treated like an Unbounded Knapsack, where each cut length can be used multiple times.
- For each cut length, we have two choices: take the cut (if it fits) or skip it.
- We choose the maximum profit from these choices to get the best way to cut the rod.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int cutRodRecur(int i, int j, vector<int> &price, vector<vector<int>> &dp)
{
// Base case
if (i == 0 || j == 0)
return 0;
// Return stored result
if (dp[i][j] != -1)
return dp[i][j];
int take = 0;
// Take a piece of length i
if (i <= j)
take = price[i - 1] + cutRodRecur(i, j - i, price, dp);
// Skip the current piece length
int noTake = cutRodRecur(i - 1, j, price, dp);
return dp[i][j] = max(take, noTake);
}
int cutRod(vector<int> &price)
{
int n = price.size();
vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
return cutRodRecur(n, n, price, dp);
}
int main()
{
vector<int> price = {1, 5, 8, 9, 10, 17, 17, 20};
cout << cutRod(price);
}
import java.util.Arrays;
public class GFG {
int cutRodRecur(int i, int j, int[] price, int[][] dp)
{
// Base case
if (i == 0 || j == 0)
return 0;
// Return stored result
if (dp[i][j] != -1)
return dp[i][j];
int take = 0;
// Take a piece of length i
if (i <= j)
take = price[i - 1]
+ cutRodRecur(i, j - i, price, dp);
// Skip the current piece length
int noTake = cutRodRecur(i - 1, j, price, dp);
return dp[i][j] = Math.max(take, noTake);
}
int cutRod(int[] price)
{
int n = price.length;
int[][] dp = new int[n + 1][n + 1];
for (int[] row : dp)
Arrays.fill(row, -1);
return cutRodRecur(n, n, price, dp);
}
public static void main(String[] args)
{
GFG rc = new GFG();
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
System.out.println(rc.cutRod(price));
}
}
def cutRodRecur(i, j, price, dp):
# Base case
if i == 0 or j == 0:
return 0
# Return stored result
if dp[i][j]!= -1:
return dp[i][j]
take = 0
# Take a piece of length i
if i <= j:
take = price[i - 1] + cutRodRecur(i, j - i, price, dp)
# Skip the current piece length
noTake = cutRodRecur(i - 1, j, price, dp)
dp[i][j] = max(take, noTake)
return dp[i][j]
def cutRod(price):
n = len(price)
dp = [[-1 for _ in range(n + 1)] for _ in range(n + 1)]
return cutRodRecur(n, n, price, dp)
if __name__ == "__main__":
price = [1, 5, 8, 9, 10, 17, 17, 20]
print(cutRod(price))
using System;
public class GFG {
public int CutRodRecur(int i, int j, int[] price,
int[, ] dp)
{
// Base case
if (i == 0 || j == 0)
return 0;
// Return stored result
if (dp[i, j] != -1)
return dp[i, j];
int take = 0;
// Take a piece of length i
if (i <= j)
take = price[i - 1]
+ CutRodRecur(i, j - i, price, dp);
// Skip the current piece length
int noTake = CutRodRecur(i - 1, j, price, dp);
return dp[i, j] = Math.Max(take, noTake);
}
public int cutRod(int[] price)
{
int n = price.Length;
int[, ] dp = new int[n + 1, n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
dp[i, j] = -1;
}
}
return CutRodRecur(n, n, price, dp);
}
public static void Main()
{
GFG rc = new GFG();
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
Console.WriteLine(rc.cutRod(price));
}
}
function cutRodRecur(i, j, price, dp)
{
// Base case
if (i === 0 || j === 0)
return 0;
// Return stored result
if (dp[i][j] !== -1)
return dp[i][j];
let take = 0;
// Take a piece of length i
if (i <= j)
take = price[i - 1]
+ cutRodRecur(i, j - i, price, dp);
// Skip the current piece length
let noTake = cutRodRecur(i - 1, j, price, dp);
return dp[i][j] = Math.max(take, noTake);
}
function cutRod(price)
{
let n = price.length;
let dp = Array.from({length : n + 1},
() => Array(n + 1).fill(-1));
return cutRodRecur(n, n, price, dp);
}
// Driver Code
let price = [ 1, 5, 8, 9, 10, 17, 17, 20 ];
console.log(cutRod(price));
Output
22
[Expected Approach - 1] Top-Down DP or Memoization - O(n ^ 2) Time and O(n) Space
- In recursion, the same rod lengths are solved multiple times.
- Since there are only n possible rod lengths, we store their results in a DP array.
- If a result is already stored, we reuse it instead of recomputing, improving efficiency.
Working of Approach:
- If the rod length is 0, return 0.
- If the result is already stored in dp[i], return it.
- Try every possible first cut and add its price to the maximum value of the remaining rod.
- Store and return the maximum value in dp[i].
Let us understand with an example:
Input: price[] = [1, 5, 8, 9, 10, 17, 17, 20]
- For each rod length i, try every first cut j and calculate price[j - 1] + cutRodRecur(i - j).
- For length 2, the maximum value is 5; for length 6, it is 17.
- For length 8, the maximum value is obtained by cutting the rod into lengths 2 and 6, giving 5 + 17 = 22.
- The computed results are stored in dp to avoid repeated calculations, and the function returns 22.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int cutRodRecur(int i, vector<int> &price, vector<int> &dp)
{
// Base case
if (i == 0)
return 0;
// Return stored result
if (dp[i] != -1)
return dp[i];
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++)
{
res = max(res, price[j - 1] + cutRodRecur(i - j, price, dp));
}
return dp[i] = res;
}
int cutRod(vector<int> &price)
{
int n = price.size();
vector<int> dp(n + 1, -1);
return cutRodRecur(n, price, dp);
}
int main()
{
vector<int> price = {1, 5, 8, 9, 10, 17, 17, 20};
cout << cutRod(price);
return 0;
}
import java.util.Arrays;
public class GFG {
// Function to find the maximum obtainable value
public static int cutRodRecur(int i, int[] price,
int[] dp)
{
// Base case
if (i == 0)
return 0;
// Return stored result
if (dp[i] != -1)
return dp[i];
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++) {
res = Math.max(
res, price[j - 1]
+ cutRodRecur(i - j, price, dp));
}
return dp[i] = res;
}
public static int cutRod(int[] price)
{
int n = price.length;
int[] dp = new int[n + 1];
Arrays.fill(dp, -1);
return cutRodRecur(n, price, dp);
}
public static void main(String[] args)
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
System.out.println(cutRod(price));
}
}
def cutRodRecur(i, price, dp):
# Base case
if i == 0:
return 0
# Return stored result
if dp[i]!= -1:
return dp[i]
res = 0
# Try every possible first cut
for j in range(1, i + 1):
res = max(res, price[j - 1] + cutRodRecur(i - j, price, dp))
dp[i] = res
return dp[i]
def cutRod(price):
n = len(price)
dp = [-1] * (n + 1)
return cutRodRecur(n, price, dp)
if __name__ == '__main__':
price = [1, 5, 8, 9, 10, 17, 17, 20]
print(cutRod(price))
using System;
public class GFG {
public static int CutRodRecur(int i, int[] price,
int[] dp)
{
// Base case
if (i == 0)
return 0;
// Return stored result
if (dp[i] != -1)
return dp[i];
int res = 0;
// Try every possible first cut
for (int j = 1; j <= i; j++) {
res = Math.Max(
res, price[j - 1]
+ CutRodRecur(i - j, price, dp));
}
return dp[i] = res;
}
public static int cutRod(int[] price)
{
int n = price.Length;
int[] dp = new int[n + 1];
Array.Fill(dp, -1);
return CutRodRecur(n, price, dp);
}
public static void Main()
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
Console.WriteLine(cutRod(price));
}
}
function cutRodRecur(i, price, dp)
{
// Base case
if (i === 0)
return 0;
// Return stored result
if (dp[i] !== -1)
return dp[i];
let res = 0;
// Try every possible first cut
for (let j = 1; j <= i; j++) {
res = Math.max(res,
price[j - 1]
+ cutRodRecur(i - j, price, dp));
}
return dp[i] = res;
}
function cutRod(price)
{
const n = price.length;
const dp = Array(n + 1).fill(-1);
return cutRodRecur(n, price, dp);
}
// Driver Code
const price = [ 1, 5, 8, 9, 10, 17, 17, 20 ];
console.log(cutRod(price));
Output
22
[Expected Approach - 2] Bottom-Up DP or Tabulation - O(n ^ 2) Time and O(n) Space
- We compute the maximum profit starting from smaller rod lengths and move to larger ones.
- For a rod of length i, we try all cuts j and (i − j).
- Since smaller lengths are already solved, we reuse their results to fill the DP table.
Detailed Steps:
- Initialize dp[0] = 0, as a rod of length 0 has no value.
- Iterate through each rod length from 1 to n.
- Try every possible first cut and add its price to the maximum value of the remaining rod.
- Store the maximum value in dp[i] and return dp[n].
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int cutRod(vector<int> &price)
{
int n = price.size();
vector<int> dp(n + 1, 0);
// Find maximum value for each rod length
for (int i = 1; i <= n; i++)
{
// Try every possible first cut
for (int j = 1; j <= i; j++)
{
dp[i] = max(dp[i], price[j - 1] + dp[i - j]);
}
}
return dp[n];
}
int main()
{
vector<int> price = {1, 5, 8, 9, 10, 17, 17, 20};
cout << cutRod(price);
return 0;
}
import java.util.*;
public class GFG {
public static int cutRod(int[] price)
{
int n = price.length;
int[] dp = new int[n + 1];
// Find maximum value for each rod length
for (int i = 1; i <= n; i++) {
// Try every possible first cut
for (int j = 1; j <= i; j++) {
dp[i] = Math.max(dp[i],
price[j - 1] + dp[i - j]);
}
}
return dp[n];
}
public static void main(String[] args)
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
System.out.println(cutRod(price));
}
}
def cutRod(price):
n = len(price)
dp = [0] * (n + 1)
# Find maximum value for each rod length
for i in range(1, n + 1):
# Try every possible first cut
for j in range(1, i + 1):
dp[i] = max(dp[i], price[j - 1] + dp[i - j])
return dp[n]
if __name__ == "__main__":
price = [1, 5, 8, 9, 10, 17, 17, 20]
print(cutRod(price))
using System;
public class GFG {
public static int cutRod(int[] price)
{
int n = price.Length;
int[] dp = new int[n + 1];
// Find maximum value for each rod length
for (int i = 1; i <= n; i++) {
// Try every possible first cut
for (int j = 1; j <= i; j++) {
dp[i] = Math.Max(dp[i],
price[j - 1] + dp[i - j]);
}
}
return dp[n];
}
public static void Main()
{
int[] price = { 1, 5, 8, 9, 10, 17, 17, 20 };
Console.WriteLine(cutRod(price));
}
}
function cutRod(price)
{
let n = price.length;
let dp = new Array(n + 1).fill(0);
// Find maximum value for each rod length
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= i; j++) {
dp[i]
= Math.max(dp[i], price[j - 1] + dp[i - j]);
}
}
return dp[n];
}
// Driver Code
let price = [ 1, 5, 8, 9, 10, 17, 17, 20 ];
console.log(cutRod(price));
Output
22