Given a rectangular paper of dimensions a x b. The task is to cut the entire paper into the minimum number of square pieces. We can choose square pieces of any size, but they must be cut without overlapping or leaving any extra space.
Examples:
Input: a = 5, b = 8
5 squares cut from Paper of size 5 X 8
Output: 5 Explanation: We can cut the paper into 5 squares: 1 square of size 5x5, 1 square of size 3x3, 1 square of size 2x2 and 2 squares of size 1x1.
Input: a = 13, b = 11
6 squares cut from Paper of size 13 X 11
Output: 6 Explanation: We can cut the paper into 6 squares: 1 square of size 7x7, 1 square of size 6x6, 1 square of size 5x5, 2 squares of size 4x4 and 1 square of size 1x1.
Input: a = 6, b = 7
5 squares cut from Paper of size 6 X 7
Output: 5 Explanation: We can cut the paper into 5 squares: 1 square of size 4x4, 2 squares of size 3x3 and 2 squares of size 3x3.
At the first sight, it might seem that the problem can be easily solved by cutting the largest square possible from the paper first, followed by cutting the largest square from the remaining paper and so on till we have cut the entire paper. But, this solution is incorrect.
Why Greedy Approach won't work?
Consider a paper of size 6x7, then if we try to cut the paper greedily we will get 7 squares: 1 square of size 6x6 and 6 squares of size 1x1, whereas the correct solution is: 5. Hence, greedy approach won't work.
[Incorrect Approach 2] Using Dynamic Programming
Dynamic Programming with vertical or horizonal cuts: Another solution which might seem correct is using Dynamic Programming. We can maintain a dp[][] table such that dp[i][j] = minimum number of squares that can be cut from paper of size i x j. Then for paper of size axb,
We can try to cut it along each row: dp[i][j] = min(dp[i][j], 1 + dp[i - k][j] + dp[k][j]), where k can be in the range [1, i - 1].
We can try to cut it along each column: dp[i][j] =min(dp[i][j], 1 + dp[i][j - k] + dp[i][k]), where k can be in the range [1, j - 1].
Finally, minimum of all cuts will be the answer. But, this solution is also incorrect.
Why cutting vertically or horizontally with Dynamic Programming Approach won't work?
This won't work because we are assuming that a vertical or horizontal cut will always divide the rectangle into two parts. Consider a paper of size 13x11, then if we try to cut the paper using DP approach, we will get 8 squares but the correct answer (as shown in Examples) is 6. Hence, Dynamic Programming won't work.
[Correct Approach] Using DFS and Dynamic Programming
The idea is to cut the entire paper using DFS in bottom-up manner. In every step, find the lowest-left corner of the paper and try to cut squares of all possible size from that corner. After cutting a square, again find the lowest-left corner of the remaining paper to cut squares of all possible sizes and so on. But if we try all possible cuts from the lowest-left corner of every possible paper size, then it would be quite inefficient. We can optimize it by using Dynamic Programming to store minimum cuts for each possible paper size.
To uniquely identify any paper size, we can maintain a remSq[] array, such that remSq[i] stores the number of remaining squares of size 1x1 in the ith column of the paper. So, for a paper of size 6x7, remSq[] = {6, 6, 6, 6, 6, 6, 6}. Also to find the lowest-left corner, we will find the first index having the maximum remaining squares. So, we can hash the value of remSq[] array to find a unique key for all the possible values of remSq[] array.
C++
// C++ Program to find minimum number of squares to cut// from a paper of size axb#include<bits/stdc++.h>usingnamespacestd;// function to get the hash key for remSq arrayintgetKey(vector<int>&remSq,intb){intbase=1;intkey=0;for(inti=0;i<b;i++){key+=(remSq[i]*base);base=base*(b+1);}returnkey;}// Recursive function to find the minimum number of square cuts// for a given remSq arrayintminCutUtil(vector<int>&remSq,inta,intb,map<int,int>&memo){// pointers to mark the start and end of range // with maximum remaining squaresintstart,end;// Check if we have previously calculated the answer// for the same stateintkey=getKey(remSq,b);if(memo.find(key)!=memo.end())returnmemo[key];intmaxRemSq=0;// Find the starting point of min heightfor(inti=0;i<b;i++){if(remSq[i]>maxRemSq){maxRemSq=remSq[i];start=i;}}// If max remaining squares = 0, then we have already// cut the entire paperif(maxRemSq==0)return0;end=start;vector<int>newRemSq=remSq;intans=INT_MAX;// Find the ending point of min heightwhile(end<b){// length of edge of square from start till current endintsquareEdge=end-start+1;// If the current column does not have maximum remaining// squares or if it's impossible to cut a square of// size squareEdge, then break out of the loopif(newRemSq[end]!=maxRemSq||newRemSq[end]-squareEdge<0)break;// If we can cut a square of size squareEdge, // update the remainingSquaresfor(inti=start;i<=end;i++)newRemSq[i]=maxRemSq-squareEdge;// Find the solution for new remainingSquaresans=min(ans,1+minCutUtil(newRemSq,a,b,memo));end+=1;}returnmemo[key]=ans;}// Function to find the minimum number of squares we can cut // using paper of size a X bintminCut(inta,intb){// if the given rectangle is a squareif(a==b)return1;// Initialize remaining squares = a for all the b columnsvector<int>remSq(b,a);map<int,int>memo;returnminCutUtil(remSq,a,b,memo);}intmain(){// Sample Inputinta=13,b=11;// Function call to get minimum number // of squares for axbcout<<minCut(a,b);return0;}
Java
// Java Program to find minimum number of squares to cut// from a paper of size axbimportjava.util.*;classGfG{// function to get the hash key for remSq arraystaticintgetKey(int[]remSq,intb){intbase=1;intkey=0;for(inti=0;i<b;i++){key+=(remSq[i]*base);base=base*(b+1);}returnkey;}// Recursive function to find the minimum number of square cuts// for a given remSq arraystaticintminCutUtil(int[]remSq,inta,intb,Map<Integer,Integer>memo){// pointers to mark the start and end of range // with maximum remaining squaresintstart=0,end;// Check if we have previously calculated the answer// for the same stateintkey=getKey(remSq,b);if(memo.containsKey(key))returnmemo.get(key);intmaxRemSq=0;// Find the starting point of min heightfor(inti=0;i<b;i++){if(remSq[i]>maxRemSq){maxRemSq=remSq[i];start=i;}}// If max remaining squares = 0, then we have already// cut the entire paperif(maxRemSq==0)return0;end=start;int[]newRemSq=Arrays.copyOf(remSq,b);intans=Integer.MAX_VALUE;// Find the ending point of min heightwhile(end<b){// length of edge of square from start till current endintsquareEdge=end-start+1;// If the current column does not have maximum remaining// squares or if it's impossible to cut a square of// size squareEdge, then break out of the loopif(newRemSq[end]!=maxRemSq||newRemSq[end]-squareEdge<0)break;// If we can cut a square of size squareEdge, // update the remainingSquaresfor(inti=start;i<=end;i++)newRemSq[i]=maxRemSq-squareEdge;// Find the solution for new remainingSquaresans=Math.min(ans,1+minCutUtil(newRemSq,a,b,memo));end+=1;}memo.put(key,ans);returnans;}// Function to find the minimum number of squares we can cut // using paper of size a X bstaticintminCut(inta,intb){// if the given rectangle is a squareif(a==b)return1;// Initialize remaining squares = a for all the b columnsint[]remSq=newint[b];Arrays.fill(remSq,a);Map<Integer,Integer>memo=newHashMap<>();returnminCutUtil(remSq,a,b,memo);}publicstaticvoidmain(String[]args){// Sample Inputinta=13,b=11;// Function call to get minimum number // of squares for axbSystem.out.println(minCut(a,b));}}
Python
# Python Program to find minimum number of squares to cut# from a paper of size axb# function to get the hash key for remSq arraydefgetKey(remSq,b):base=1key=0foriinrange(b):key+=remSq[i]*basebase=base*(b+1)returnkey# Recursive function to find the minimum number of square cuts# for a given remSq arraydefminCutUtil(remSq,a,b,memo):# pointers to mark the start and end of range # with maximum remaining squaresstart=0# Check if we have previously calculated the answer# for the same statekey=getKey(remSq,b)ifkeyinmemo:returnmemo[key]maxRemSq=0# Find the starting point of min heightforiinrange(b):ifremSq[i]>maxRemSq:maxRemSq=remSq[i]start=i# If max remaining squares = 0, then we have already# cut the entire paperifmaxRemSq==0:return0end=startnewRemSq=remSq[:]ans=float('inf')# Find the ending point of min heightwhileend<b:# length of edge of square from start till current endsquareEdge=end-start+1# If the current column does not have maximum remaining# squares or if it's impossible to cut a square of# size squareEdge, then break out of the loopifnewRemSq[end]!=maxRemSqor \
newRemSq[end]-squareEdge<0:break# If we can cut a square of size squareEdge, # update the remainingSquaresforiinrange(start,end+1):newRemSq[i]=maxRemSq-squareEdge# Find the solution for new remainingSquaresans=min(ans,1+minCutUtil(newRemSq,a,b,memo))end+=1memo[key]=ansreturnans# Function to find the minimum number of squares we can cut # using paper of size a X bdefminCut(a,b):# if the given rectangle is a squareifa==b:return1# Initialize remaining squares = a for all the b columnsremSq=[a]*bmemo={}returnminCutUtil(remSq,a,b,memo)if__name__=="__main__":# Sample Inputa=13b=11# Function call to get minimum number # of squares for axbprint(minCut(a,b))
C#
// C# Program to find minimum number of squares to cut// from a paper of size axbusingSystem;usingSystem.Collections.Generic;classGfG{// function to get the hash key for remSq arraystaticintgetKey(int[]remSq,intb){intbaseVal=1;intkey=0;for(inti=0;i<b;i++){key+=(remSq[i]*baseVal);baseVal=baseVal*(b+1);}returnkey;}// Recursive function to find the minimum number of square cuts// for a given remSq arraystaticintminCutUtil(int[]remSq,inta,intb,Dictionary<int,int>memo){// pointers to mark the start and end of range // with maximum remaining squaresintstart=0,end;// Check if we have previously calculated the answer// for the same stateintkey=getKey(remSq,b);if(memo.ContainsKey(key))returnmemo[key];intmaxRemSq=0;// Find the starting point of min heightfor(inti=0;i<b;i++){if(remSq[i]>maxRemSq){maxRemSq=remSq[i];start=i;}}// If max remaining squares = 0, then we have already// cut the entire paperif(maxRemSq==0)return0;end=start;int[]newRemSq=(int[])remSq.Clone();intans=int.MaxValue;// Find the ending point of min heightwhile(end<b){// length of edge of square from start till current endintsquareEdge=end-start+1;// If the current column does not have maximum remaining// squares or if it's impossible to cut a square of// size squareEdge, then break out of the loopif(newRemSq[end]!=maxRemSq||newRemSq[end]-squareEdge<0)break;// If we can cut a square of size squareEdge, // update the remainingSquaresfor(inti=start;i<=end;i++)newRemSq[i]=maxRemSq-squareEdge;// Find the solution for new remainingSquaresans=Math.Min(ans,1+minCutUtil(newRemSq,a,b,memo));end+=1;}memo[key]=ans;returnans;}// Function to find the minimum number of squares we can cut // using paper of size a X bstaticintminCut(inta,intb){// if the given rectangle is a squareif(a==b)return1;// Initialize remaining squares = a for all the b columnsint[]remSq=newint[b];for(inti=0;i<b;i++)remSq[i]=a;Dictionary<int,int>memo=newDictionary<int,int>();returnminCutUtil(remSq,a,b,memo);}staticvoidMain(){inta=13,b=11;// Function call to get minimum number // of squares for axbConsole.WriteLine(minCut(a,b));}}
JavaScript
// JavaScript Program to find minimum number of squares to cut// from a paper of size axb// function to get the hash key for remSq arrayfunctiongetKey(remSq,b){letbase=1;letkey=0;for(leti=0;i<b;i++){key+=(remSq[i]*base);base=base*(b+1);}returnkey;}// Recursive function to find the minimum number of square cuts// for a given remSq arrayfunctionminCutUtil(remSq,a,b,memo){// pointers to mark the start and end of range // with maximum remaining squaresletstart=0,end;// Check if we have previously calculated the answer// for the same stateletkey=getKey(remSq,b);if(keyinmemo)returnmemo[key];letmaxRemSq=0;// Find the starting point of min heightfor(leti=0;i<b;i++){if(remSq[i]>maxRemSq){maxRemSq=remSq[i];start=i;}}// If max remaining squares = 0, then we have already// cut the entire paperif(maxRemSq===0)return0;end=start;letnewRemSq=remSq.slice();letans=Infinity;// Find the ending point of min heightwhile(end<b){// length of edge of square from start till current endletsquareEdge=end-start+1;// If the current column does not have maximum remaining// squares or if it's impossible to cut a square of// size squareEdge, then break out of the loopif(newRemSq[end]!==maxRemSq||newRemSq[end]-squareEdge<0)break;// If we can cut a square of size squareEdge, // update the remainingSquaresfor(leti=start;i<=end;i++)newRemSq[i]=maxRemSq-squareEdge;// Find the solution for new remainingSquaresans=Math.min(ans,1+minCutUtil(newRemSq,a,b,memo));end+=1;}memo[key]=ans;returnans;}// Function to find the minimum number of squares we can cut // using paper of size a X bfunctionminCut(a,b){// if the given rectangle is a squareif(a===b)return1;// Initialize remaining squares = a for all the b columnsletremSq=newArray(b).fill(a);letmemo={};returnminCutUtil(remSq,a,b,memo);}// Driver Codeleta=13,b=11;// Function call to get minimum number // of squares for axbconsole.log(minCut(a,b));
Output
6
Time Complexity: O(a^b), for each of b columns, we can have a squares. Auxiliary Space: O(a^b), due to memoization storing each unique state.