Open In App

Paper Cut into Minimum Number of Squares

Last Updated : 19 Apr, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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

Paper-cut-into-minimum-number-of-squares-1
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

Paper-cut-into-minimum-number-of-squares-2
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

Paper-cut-into-minimum-number-of-squares-3
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.

[Incorrect Approach 1] Using Greedy Technique

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>
using namespace std;

// function to get the hash key for remSq array
int getKey(vector<int> &remSq, int b) {
    int base = 1;
    int key = 0;
    for (int i = 0; i < b; i++)
    {
        key += (remSq[i] * base);
        base = base * (b + 1);
    }
    return key;
}

// Recursive function to find the minimum number of square cuts
// for a given remSq array
int minCutUtil(vector<int> &remSq, int a, int b, 
               map<int, int> &memo) {

    // pointers to mark the start and end of range 
    // with maximum remaining squares
    int start, end;

    // Check if we have previously calculated the answer
    // for the same state
    int key = getKey(remSq, b);
    if (memo.find(key) != memo.end())
        return memo[key];

    int maxRemSq = 0;

    // Find the starting point of min height
    for (int i = 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 paper
    if (maxRemSq == 0)
        return 0;

    end = start;
    vector<int> newRemSq = remSq;

    int ans = INT_MAX;

    // Find the ending point of min height
    while (end < b) {

        // length of edge of square from start till current end
        int squareEdge = 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 loop
        if (newRemSq[end] != maxRemSq || 
            newRemSq[end] - squareEdge < 0)
            break;

        // If we can cut a square of size squareEdge, 
        // update the remainingSquares
        for (int i = start; i <= end; i++)
            newRemSq[i] = maxRemSq - squareEdge;

        // Find the solution for new remainingSquares
        ans = min(ans, 1 + minCutUtil(newRemSq, a, b, memo));
        end += 1;
    }

    return memo[key] = ans;
}

// Function to find the minimum number of squares we can cut 
// using paper of size a X b
int minCut(int a, int b) {

    // if the given rectangle is a square
    if (a == b)
        return 1;

    // Initialize remaining squares = a for all the b columns
    vector<int> remSq(b, a);

    map<int, int> memo;
    return minCutUtil(remSq, a, b, memo);
}

int main() {

    // Sample Input
    int a = 13, b = 11;

    // Function call to get minimum number 
    // of squares for axb
    cout << minCut(a, b);
    return 0;
}
Java
// Java Program to find minimum number of squares to cut
// from a paper of size axb
import java.util.*;

class GfG {

    // function to get the hash key for remSq array
    static int getKey(int[] remSq, int b) {
        int base = 1;
        int key = 0;
        for (int i = 0; i < b; i++) {
            key += (remSq[i] * base);
            base = base * (b + 1);
        }
        return key;
    }

    // Recursive function to find the minimum number of square cuts
    // for a given remSq array
    static int minCutUtil(int[] remSq, int a, int b,
                          Map<Integer, Integer> memo) {

        // pointers to mark the start and end of range 
        // with maximum remaining squares
        int start = 0, end;

        // Check if we have previously calculated the answer
        // for the same state
        int key = getKey(remSq, b);
        if (memo.containsKey(key))
            return memo.get(key);

        int maxRemSq = 0;

        // Find the starting point of min height
        for (int i = 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 paper
        if (maxRemSq == 0)
            return 0;

        end = start;
        int[] newRemSq = Arrays.copyOf(remSq, b);

        int ans = Integer.MAX_VALUE;

        // Find the ending point of min height
        while (end < b) {

            // length of edge of square from start till current end
            int squareEdge = 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 loop
            if (newRemSq[end] != maxRemSq ||
                newRemSq[end] - squareEdge < 0)
                break;

            // If we can cut a square of size squareEdge, 
            // update the remainingSquares
            for (int i = start; i <= end; i++)
                newRemSq[i] = maxRemSq - squareEdge;

            // Find the solution for new remainingSquares
            ans = Math.min(ans, 1 + minCutUtil(newRemSq, a, b, memo));
            end += 1;
        }

        memo.put(key, ans);
        return ans;
    }

    // Function to find the minimum number of squares we can cut 
    // using paper of size a X b
    static int minCut(int a, int b) {

        // if the given rectangle is a square
        if (a == b)
            return 1;

        // Initialize remaining squares = a for all the b columns
        int[] remSq = new int[b];
        Arrays.fill(remSq, a);

        Map<Integer, Integer> memo = new HashMap<>();
        return minCutUtil(remSq, a, b, memo);
    }

    public static void main(String[] args) {

        // Sample Input
        int a = 13, b = 11;

        // Function call to get minimum number 
        // of squares for axb
        System.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 array
def getKey(remSq, b):
    base = 1
    key = 0
    for i in range(b):
        key += remSq[i] * base
        base = base * (b + 1)
    return key

# Recursive function to find the minimum number of square cuts
# for a given remSq array
def minCutUtil(remSq, a, b, memo):

    # pointers to mark the start and end of range 
    # with maximum remaining squares
    start = 0

    # Check if we have previously calculated the answer
    # for the same state
    key = getKey(remSq, b)
    if key in memo:
        return memo[key]

    maxRemSq = 0

    # Find the starting point of min height
    for i in range(b):
        if remSq[i] > maxRemSq:
            maxRemSq = remSq[i]
            start = i

    # If max remaining squares = 0, then we have already
    # cut the entire paper
    if maxRemSq == 0:
        return 0

    end = start
    newRemSq = remSq[:]

    ans = float('inf')

    # Find the ending point of min height
    while end < b:

        # length of edge of square from start till current end
        squareEdge = 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 loop
        if newRemSq[end] != maxRemSq or \
           newRemSq[end] - squareEdge < 0:
            break

        # If we can cut a square of size squareEdge, 
        # update the remainingSquares
        for i in range(start, end + 1):
            newRemSq[i] = maxRemSq - squareEdge

        # Find the solution for new remainingSquares
        ans = min(ans, 1 + minCutUtil(newRemSq, a, b, memo))
        end += 1

    memo[key] = ans
    return ans

# Function to find the minimum number of squares we can cut 
# using paper of size a X b
def minCut(a, b):

    # if the given rectangle is a square
    if a == b:
        return 1

    # Initialize remaining squares = a for all the b columns
    remSq = [a] * b

    memo = {}
    return minCutUtil(remSq, a, b, memo)

if __name__ == "__main__":

    # Sample Input
    a = 13
    b = 11

    # Function call to get minimum number 
    # of squares for axb
    print(minCut(a, b))
C#
// C# Program to find minimum number of squares to cut
// from a paper of size axb
using System;
using System.Collections.Generic;

class GfG {

    // function to get the hash key for remSq array
    static int getKey(int[] remSq, int b) {
        int baseVal = 1;
        int key = 0;
        for (int i = 0; i < b; i++) {
            key += (remSq[i] * baseVal);
            baseVal = baseVal * (b + 1);
        }
        return key;
    }

    // Recursive function to find the minimum number of square cuts
    // for a given remSq array
    static int minCutUtil(int[] remSq, int a, int b,
                          Dictionary<int, int> memo) {

        // pointers to mark the start and end of range 
        // with maximum remaining squares
        int start = 0, end;

        // Check if we have previously calculated the answer
        // for the same state
        int key = getKey(remSq, b);
        if (memo.ContainsKey(key))
            return memo[key];

        int maxRemSq = 0;

        // Find the starting point of min height
        for (int i = 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 paper
        if (maxRemSq == 0)
            return 0;

        end = start;
        int[] newRemSq = (int[])remSq.Clone();

        int ans = int.MaxValue;

        // Find the ending point of min height
        while (end < b) {

            // length of edge of square from start till current end
            int squareEdge = 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 loop
            if (newRemSq[end] != maxRemSq ||
                newRemSq[end] - squareEdge < 0)
                break;

            // If we can cut a square of size squareEdge, 
            // update the remainingSquares
            for (int i = start; i <= end; i++)
                newRemSq[i] = maxRemSq - squareEdge;

            // Find the solution for new remainingSquares
            ans = Math.Min(ans, 1 + minCutUtil(newRemSq, a, b, memo));
            end += 1;
        }

        memo[key] = ans;
        return ans;
    }

    // Function to find the minimum number of squares we can cut 
    // using paper of size a X b
    static int minCut(int a, int b) {

        // if the given rectangle is a square
        if (a == b)
            return 1;

        // Initialize remaining squares = a for all the b columns
        int[] remSq = new int[b];
        for (int i = 0; i < b; i++) remSq[i] = a;

        Dictionary<int, int> memo = new Dictionary<int, int>();
        return minCutUtil(remSq, a, b, memo);
    }

    static void Main() {

        int a = 13, b = 11;

        // Function call to get minimum number 
        // of squares for axb
        Console.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 array
function getKey(remSq, b) {
    let base = 1;
    let key = 0;
    for (let i = 0; i < b; i++) {
        key += (remSq[i] * base);
        base = base * (b + 1);
    }
    return key;
}

// Recursive function to find the minimum number of square cuts
// for a given remSq array
function minCutUtil(remSq, a, b, memo) {

    // pointers to mark the start and end of range 
    // with maximum remaining squares
    let start = 0, end;

    // Check if we have previously calculated the answer
    // for the same state
    let key = getKey(remSq, b);
    if (key in memo)
        return memo[key];

    let maxRemSq = 0;

    // Find the starting point of min height
    for (let i = 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 paper
    if (maxRemSq === 0)
        return 0;

    end = start;
    let newRemSq = remSq.slice();

    let ans = Infinity;

    // Find the ending point of min height
    while (end < b) {

        // length of edge of square from start till current end
        let squareEdge = 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 loop
        if (newRemSq[end] !== maxRemSq ||
            newRemSq[end] - squareEdge < 0)
            break;

        // If we can cut a square of size squareEdge, 
        // update the remainingSquares
        for (let i = start; i <= end; i++)
            newRemSq[i] = maxRemSq - squareEdge;

        // Find the solution for new remainingSquares
        ans = Math.min(ans, 1 + minCutUtil(newRemSq, a, b, memo));
        end += 1;
    }

    memo[key] = ans;
    return ans;
}

// Function to find the minimum number of squares we can cut 
// using paper of size a X b
function minCut(a, b) {

    // if the given rectangle is a square
    if (a === b)
        return 1;

    // Initialize remaining squares = a for all the b columns
    let remSq = new Array(b).fill(a);

    let memo = {};
    return minCutUtil(remSq, a, b, memo);
}

// Driver Code
let a = 13, b = 11;

// Function call to get minimum number 
// of squares for axb
console.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.


Article Tags :
Practice Tags :

Similar Reads