Given a wooden stick of length n units. The stick is labeled from 0 to n. We are also given an integer array cuts[], where each element cuts[i] represents the position along the stick at which we can make a cut.
Each cut costs the length of the stick being cut, and after every cut, the stick is divided into two smaller sticks. We can perform the cuts in any order, determine the minimum total cost required to perform all cuts.
Examples:
Input: n = 10, cuts[] = [2, 4, 7] Output: 20 Explanation: If we cut the stick in the order [4, 2, 7], it will give the minimum total cost.
Input: n = 8, cuts[] = [1, 6, 3, 5] Output: 19 Explanation: If we cut the stick in the order [3, 6, 1, 5], it will give the minimum total cost.
In this problem, we have multiple possibilities for where we can make the first cut. Once we cut the stick at a certain position, it divides into two smaller parts and each of those parts again becomes a smaller version of the same problem. Because we have to explore all possible ways of cutting and find the one that gives the minimum total cost, using recursion becomes a very natural and intuitive choice here.
The idea is like this - first, we include 0 and n in the cuts array because these represent the two ends of the stick. This helps define every segment clearly between two valid cut positions. Since the order of cuts matters, and we also need to easily calculate the length of any segment, we sort the cuts array. Sorting allows us to directly find the current stick’s length using the difference (right - left). After sorting, we explore all possible ways to make a cut between left and right. If we make a cut at position cuts[i], the stick splits into two smaller subproblems - one from left to cuts[i] and another from cuts[i] to right. We then solve both subproblems recursively to find their minimum cost.
We will stop the recursion when there are no more possible cuts between left and right, that means when the segment length becomes ≤ 1. In that case, no further cuts are possible, so the cost is 0.
C++
//Driver Code Starts#include<iostream>#include<vector>#include<climits>#include<algorithm>usingnamespacestd;//Driver Code EndsintminCostRec(intleft,intright,vector<int>&cuts){// Base caseif(right-left<=1)return0;intminCost=INT_MAX;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts)+minCostRec(i,right,cuts);minCost=min(minCost,cost);}// If no valid cut found, cost is 0return(minCost==INT_MAX)?0:minCost;}intminCutCost(intn,vector<int>&cuts){// Add boundaries 0 and n to represent stick endscuts.push_back(0);cuts.push_back(n);// Sort to ensure segments are in proper ordersort(cuts.begin(),cuts.end());returnminCostRec(0,cuts.size()-1,cuts);}//Driver Code Startsintmain(){intn=10;vector<int>cuts={2,4,7};cout<<minCutCost(n,cuts);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.ArrayList;importjava.util.Collections;publicclassGFG{//Driver Code EndsstaticintminCostRec(intleft,intright,ArrayList<Integer>cutsList){// Base caseif(right-left<=1)return0;intminCost=Integer.MAX_VALUE;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cutsList.get(right)-cutsList.get(left)+minCostRec(left,i,cutsList)+minCostRec(i,right,cutsList);minCost=Math.min(minCost,cost);}// If no valid cut found, cost is 0return(minCost==Integer.MAX_VALUE)?0:minCost;}staticintminCutCost(intn,int[]cuts){ArrayList<Integer>cutsList=newArrayList<>();for(intcut:cuts)cutsList.add(cut);// Add boundaries 0 and n to represent stick endscutsList.add(0);cutsList.add(n);// Sort to ensure segments are in proper orderCollections.sort(cutsList);returnminCostRec(0,cutsList.size()-1,cutsList);}//Driver Code Startspublicstaticvoidmain(String[]args){intn=10;int[]cuts={2,4,7};System.out.println(minCutCost(n,cuts));}}//Driver Code Ends
Python
defminCostRec(left,right,cuts):# Base caseifright-left<=1:return0minCost=float('inf')# Explore all possible cuts between left and rightforiinrange(left+1,right):cost=cuts[right]-cuts[left]+minCostRec(left,i,cuts)+minCostRec(i,right,cuts)minCost=min(minCost,cost)# If no valid cut found, cost is 0return0ifminCost==float('inf')elseminCostdefminCutCost(n,cuts):# Add boundaries 0 and n to represent stick endscuts.append(0)cuts.append(n)# Sort to ensure segments are in proper ordercuts.sort()returnminCostRec(0,len(cuts)-1,cuts)#Driver Code Startsif__name__=="__main__":n=10cuts=[2,4,7]print(minCutCost(n,cuts))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;classGFG{//Driver Code EndsstaticintminCostRec(intleft,intright,List<int>cutsList){// Base caseif(right-left<=1)return0;intminCost=int.MaxValue;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cutsList[right]-cutsList[left]+minCostRec(left,i,cutsList)+minCostRec(i,right,cutsList);minCost=Math.Min(minCost,cost);}// If no valid cut found, cost is 0return(minCost==int.MaxValue)?0:minCost;}staticintminCutCost(intn,int[]cuts){List<int>cutsList=newList<int>(cuts);// Add boundaries 0 and n to represent stick endscutsList.Add(0);cutsList.Add(n);// Sort to ensure segments are in proper ordercutsList.Sort();returnminCostRec(0,cutsList.Count-1,cutsList);}//Driver Code StartsstaticvoidMain(){intn=10;int[]cuts={2,4,7};Console.WriteLine(minCutCost(n,cuts));}}//Driver Code Ends
JavaScript
functionminCostRec(left,right,cuts){// Base caseif(right-left<=1)return0;letminCost=Infinity;// Explore all possible cuts between left and rightfor(leti=left+1;i<right;i++){letcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts)+minCostRec(i,right,cuts);minCost=Math.min(minCost,cost);}// If no valid cut found, cost is 0returnminCost===Infinity?0:minCost;}functionminCutCost(n,cuts){// Add boundaries 0 and n to represent stick endscuts.push(0);cuts.push(n);// Sort to ensure segments are in proper ordercuts.sort((a,b)=>a-b);returnminCostRec(0,cuts.length-1,cuts);}//Driver Code Starts//Driver Codeletn=10;letcuts=[2,4,7];console.log(minCutCost(n,cuts));//Driver Code Ends
Output
20
Time Complexity: O(k!), where k is the number of cuts because we explore all possible orders of cuts. Auxiliary Space: O(k) – due to the recursion stack.
[Expected Approach - 1] Using Memoization (Top Down)
In the recursive approach, we noticed that many subproblems are solved multiple times. For example, the cost to cut a segment [left, right] is computed repeatedly whenever this segment appears in different recursive paths. To avoid this repeated work, we use memoization (DP).
The idea is simple: whenever we compute the minimum cost for a segment [left, right], we store the result in a DP table. We can create a 2D DP table where dp[i][j] represents the minimum cost to cut the stick between the i-th and j-th cut positions. Before performing the recursion for a segment, we check if dp[i][j] has already been computed. If yes, we return it directly; otherwise, we compute and store it. This reduces the exponential recursive solution to a polynomial time solution.
C++
//Driver Code Starts#include<iostream>#include<vector>#include<climits>#include<algorithm>usingnamespacestd;//Driver Code EndsintminCostRec(intleft,intright,vector<int>&cuts,vector<vector<int>>&dp){// Base caseif(right-left<=1)return0;// Return stored result if already computedif(dp[left][right]!=-1)returndp[left][right];intminCost=INT_MAX;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts,dp)+minCostRec(i,right,cuts,dp);minCost=min(minCost,cost);}returndp[left][right]=(minCost==INT_MAX)?0:minCost;}intminCutCost(intn,vector<int>&cuts){// Add boundaries 0 and n to represent stick endscuts.push_back(0);cuts.push_back(n);// Sort to ensure segments are in proper ordersort(cuts.begin(),cuts.end());intsz=cuts.size();//for storing the previously computed result vector<vector<int>>dp(sz,vector<int>(sz,-1));returnminCostRec(0,sz-1,cuts,dp);}//Driver Code Startsintmain(){intn=10;vector<int>cuts={2,4,7};cout<<minCutCost(n,cuts);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.ArrayList;importjava.util.Arrays;importjava.util.Collections;publicclassGFG{//Driver Code EndsstaticintminCostRec(intleft,intright,int[]cuts,int[][]dp){// Base caseif(right-left<=1)return0;// Return stored result if already computedif(dp[left][right]!=-1)returndp[left][right];intminCost=Integer.MAX_VALUE;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts,dp)+minCostRec(i,right,cuts,dp);minCost=Math.min(minCost,cost);}returndp[left][right]=(minCost==Integer.MAX_VALUE)?0:minCost;}staticintminCutCost(intn,int[]cuts){// Create ArrayList to add boundaries 0 and nArrayList<Integer>arrayList=newArrayList<>();for(intc:cuts)arrayList.add(c);arrayList.add(0);arrayList.add(n);// Sort to ensure segments are in proper orderCollections.sort(arrayList);intsz=arrayList.size();int[]sortedCuts=newint[sz];for(inti=0;i<sz;i++)sortedCuts[i]=arrayList.get(i);// for storing the previously computed result int[][]dp=newint[sz][sz];for(int[]row:dp)Arrays.fill(row,-1);returnminCostRec(0,sz-1,sortedCuts,dp);}//Driver Code Startspublicstaticvoidmain(String[]args){intn=10;int[]cuts={2,4,7};System.out.println(minCutCost(n,cuts));}}//Driver Code Ends
Python
#Driver Code Startsimportsys#Driver Code EndsdefminCostRec(left,right,cuts,dp):# Base caseifright-left<=1:return0# Return stored result if already computedifdp[left][right]!=-1:returndp[left][right]minCost=float('inf')# Explore all possible cuts between left and rightforiinrange(left+1,right):cost=cuts[right]-cuts[left]+minCostRec(left,i,cuts,dp)+minCostRec(i,right,cuts,dp)minCost=min(minCost,cost)dp[left][right]=0ifminCost==float('inf')elseminCostreturndp[left][right]defminCutCost(n,cuts):# Add boundaries 0 and n to represent stick endscuts.append(0)cuts.append(n)# Sort to ensure segments are in proper ordercuts.sort()sz=len(cuts)# for storing the previously computed result dp=[[-1for_inrange(sz)]for_inrange(sz)]returnminCostRec(0,sz-1,cuts,dp)#Driver Code Startsif__name__=="__main__":n=10cuts=[2,4,7]print(minCutCost(n,cuts))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;publicclassGFG{//Driver Code EndsstaticintminCostRec(intleft,intright,int[]cuts,int[,]dp){// Base caseif(right-left<=1)return0;// Return stored result if already computedif(dp[left,right]!=-1)returndp[left,right];intminCost=int.MaxValue;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts,dp)+minCostRec(i,right,cuts,dp);minCost=Math.Min(minCost,cost);}dp[left,right]=(minCost==int.MaxValue)?0:minCost;returndp[left,right];}staticintminCutCost(intn,int[]cuts){// Create List to add boundaries 0 and nList<int>list=newList<int>(cuts);list.Add(0);list.Add(n);// Sort to ensure segments are in proper orderlist.Sort();intsz=list.Count;int[]sortedCuts=list.ToArray();// for storing the previously computed result int[,]dp=newint[sz,sz];for(inti=0;i<sz;i++)for(intj=0;j<sz;j++)dp[i,j]=-1;returnminCostRec(0,sz-1,sortedCuts,dp);}//Driver Code StartspublicstaticvoidMain(){intn=10;int[]cuts={2,4,7};Console.WriteLine(minCutCost(n,cuts));}}//Driver Code Ends
JavaScript
functionminCostRec(left,right,cuts,dp){// Base caseif(right-left<=1)return0;// Return stored result if already computedif(dp[left][right]!==-1)returndp[left][right];letminCost=Number.MAX_SAFE_INTEGER;// Explore all possible cuts between left and rightfor(leti=left+1;i<right;i++){letcost=cuts[right]-cuts[left]+minCostRec(left,i,cuts,dp)+minCostRec(i,right,cuts,dp);minCost=Math.min(minCost,cost);}dp[left][right]=(minCost===Number.MAX_SAFE_INTEGER)?0:minCost;returndp[left][right];}functionminCutCost(n,cuts){// Add boundaries 0 and n to represent stick endscuts.push(0);cuts.push(n);// Sort to ensure segments are in proper ordercuts.sort((a,b)=>a-b);constsz=cuts.length;// for storing the previously computed result constdp=Array.from({length:sz},()=>Array(sz).fill(-1));returnminCostRec(0,sz-1,cuts,dp);}//Driver Code Starts// Driver Codeconstn=10;constcuts=[2,4,7];console.log(minCutCost(n,cuts));//Driver Code Ends
Output
20
Time complexity: O(k3), where k is the number of cuts. Auxiliary Space: O(k2)
[Expected Approach - 2] Using Bottom-Up (Tabulation)
In the memoization approach, we used recursion along with a DP table to store results of subproblems so that we don’t recompute them again and again. Although this reduced the number of repeated calculations, it still relied on recursive calls, which can lead to extra stack usage and overhead. To overcome this, we switch to a completely iterative (bottom-up) method known as Tabulation.
The idea is similar, here instead of making recursive calls, we iteratively build the DP table from smaller segments to larger ones. We start with the smallest possible segments (where no cuts can be made), and for these, the cost is naturally 0. Then, we gradually move to larger segments - at each step, combining the results of smaller parts that have already been computed and stored in the DP table. This ensures that whenever we calculate the cost for a bigger segment, the results of all its sub-segments are readily available. By the end, the DP table contains the minimum cost for every possible segment.
C++
//Driver Code Starts#include<iostream>#include<vector>#include<climits>#include<algorithm>usingnamespacestd;//Driver Code EndsintminCutCost(intn,vector<int>&cuts){// Add boundaries 0 and n to represent stick endscuts.push_back(0);cuts.push_back(n);// Sort to ensure segments are in proper ordersort(cuts.begin(),cuts.end());intsz=cuts.size();vector<vector<int>>dp(sz,vector<int>(sz,0));for(intlen=2;len<sz;len++){for(intleft=0;left+len<sz;left++){intright=left+len;intminCost=INT_MAX;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=cuts[right]-cuts[left]+dp[left][i]+dp[i][right];minCost=min(minCost,cost);}// Store the minimum cost for this segmentdp[left][right]=(minCost==INT_MAX)?0:minCost;}}// Final result is the cost to cut the full stick (0 to sz - 1)returndp[0][sz-1];}//Driver Code Startsintmain(){intn=10;vector<int>cuts={2,4,7};cout<<minCutCost(n,cuts);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.ArrayList;importjava.util.Collections;classGFG{//Driver Code EndsstaticintminCutCost(intn,int[]cuts){ArrayList<Integer>list=newArrayList<>();for(intc:cuts)list.add(c);// Add boundaries 0 and n to represent stick endslist.add(0);list.add(n);// Sort to ensure segments are in proper orderCollections.sort(list);intsz=list.size();int[][]dp=newint[sz][sz];for(intlen=2;len<sz;len++){for(intleft=0;left+len<sz;left++){intright=left+len;intminCost=Integer.MAX_VALUE;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=list.get(right)-list.get(left)+dp[left][i]+dp[i][right];minCost=Math.min(minCost,cost);}// Store the minimum cost for this segmentdp[left][right]=(minCost==Integer.MAX_VALUE)?0:minCost;}}// Final result is the cost to cut the full stick (0 to sz - 1)returndp[0][sz-1];}//Driver Code Startspublicstaticvoidmain(String[]args){intn=10;int[]cuts={2,4,7};System.out.println(minCutCost(n,cuts));}}//Driver Code Ends
Python
defminCutCost(n,cuts):# Add boundaries 0 and n to represent stick endscuts.append(0)cuts.append(n)# Sort to ensure segments are in proper ordercuts.sort()sz=len(cuts)# Initialize dp table with all zerosdp=[[0]*szfor_inrange(sz)]forlengthinrange(2,sz):forleftinrange(sz-length):right=left+lengthminCost=float('inf')# Explore all possible cuts between left and rightforiinrange(left+1,right):cost=cuts[right]-cuts[left]+dp[left][i]+dp[i][right]minCost=min(minCost,cost)# Store the minimum cost for this segmentdp[left][right]=0ifminCost==float('inf')elseminCost# Final result is the cost to cut the full stick (0 to sz - 1)returndp[0][sz-1]#Driver Code Startsif__name__=="__main__":n=10cuts=[2,4,7]print(minCutCost(n,cuts))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;classGFG{//Driver Code EndsstaticintminCutCost(intn,int[]cuts){List<int>list=newList<int>(cuts);// Add boundaries 0 and n to represent stick endslist.Add(0);list.Add(n);// Sort to ensure segments are in proper orderlist.Sort();intsz=list.Count;int[,]dp=newint[sz,sz];for(intlen=2;len<sz;len++){for(intleft=0;left+len<sz;left++){intright=left+len;intminCost=int.MaxValue;// Explore all possible cuts between left and rightfor(inti=left+1;i<right;i++){intcost=list[right]-list[left]+dp[left,i]+dp[i,right];minCost=Math.Min(minCost,cost);}// Store the minimum cost for this segmentdp[left,right]=(minCost==int.MaxValue)?0:minCost;}}// Final result is the cost to cut the full stick (0 to sz - 1)returndp[0,sz-1];}//Driver Code StartsstaticvoidMain(){intn=10;int[]cuts={2,4,7};Console.WriteLine(minCutCost(n,cuts));}}//Driver Code Ends
JavaScript
functionminCutCost(n,cuts){// Add boundaries 0 and n to represent stick endscuts.push(0);cuts.push(n);// Sort to ensure segments are in proper ordercuts.sort((a,b)=>a-b);constsz=cuts.length;constdp=Array.from({length:sz},()=>Array(sz).fill(0));for(letlen=2;len<sz;len++){for(letleft=0;left+len<sz;left++){constright=left+len;letminCost=Number.MAX_SAFE_INTEGER;// Explore all possible cuts between left and rightfor(leti=left+1;i<right;i++){constcost=cuts[right]-cuts[left]+dp[left][i]+dp[i][right];minCost=Math.min(minCost,cost);}// Store the minimum cost for this segmentdp[left][right]=(minCost===Number.MAX_SAFE_INTEGER)?0:minCost;}}// Final result is the cost to cut the full stick (0 to sz - 1)returndp[0][sz-1];}//Driver Code Starts// Driver Codeconstn=10;constcuts=[2,4,7];console.log(minCutCost(n,cuts));//Driver Code Ends
Output
20
Time complexity: O(k3), where k is the number of cuts. Auxiliary Space: O(k2)
Why Space Optimization Is Not Possible in This Problem?
In most Dynamic Programming problems, we can often reduce space complexity by observing that the current state only depends on a limited number of previous states. This allows us to reuse memory - keeping only one or two previous rows instead of the entire DP table. However, in this problem, the dependency pattern is fundamentally different. Each state dp[left][right] represents the minimum cost to cut the stick between cuts[left] and cuts[right]. To compute this value, we must explore all possible cuts between left and right, and for every possible cut position i, we rely on two non-adjacent subproblems — dp[left][i] and dp[i][right]. This means:
There is no fixed transition direction (like top-down or left-right).
Each state can depend on many scattered intervals that are spread across the DP table.
These dependencies can overlap in unpredictable patterns, making it impossible to safely overwrite previous results.
Because of this complex interval dependency, we cannot discard previously computed values or compress the DP table into fewer rows/columns without losing essential data required for future calculations.