Cut rectangle in minimum number of squares

前端 未结 6 1437
-上瘾入骨i
-上瘾入骨i 2020-12-28 17:29

I\'m trying to solve the following problem:

A rectangular paper sheet of M*N is to be cut down into squares such that:

  1. The paper i
相关标签:
6条回答
  • 2020-12-28 18:07

    This is essentially classic integer or 0-1 knapsack problem that can be solved using greedy or dynamic programming approach. You may refer to: Solving the Integer Knapsack

    0 讨论(0)
  • 2020-12-28 18:09

    This problem can be solved using dynamic programming.

    Assuming we have a rectangle with width is N and height is M.

    • if (N == M), so it is a square and nothing need to be done.

    • Otherwise, we can divide the rectangle into two other smaller one (N - x, M) and (x,M), so it can be solved recursively.

    • Similarly, we can also divide it into (N , M - x) and (N, x)

    Pseudo code:

    int[][]dp;
    boolean[][]check;
    int cutNeeded(int n, int m)
    
        if(n == m)
           return 1;
        if(check[n][m])
           return dp[n][m];
        check[n][m] = true;
        int result = n*m;
        for(int i = 1; i <= n/2; i++)
           int tmp = cutNeeded(n - i, m) + cutNeeded(i,m);
           result = min(tmp, result); 
    
        for(int i = 1; i <= m/2; i++)
           int tmp = cutNeeded(n , m - i) + cutNeeded(n,i);
           result = min(tmp, result); 
        return dp[n][m] = result;
    
    0 讨论(0)
  • 2020-12-28 18:11

    The greedy algorithm is not optimal. On a 6x5 rectangle, it uses a 5x5 square and 5 1x1 squares. The optimal solution uses 2 3x3 squares and 3 2x2 squares.

    To get an optimal solution, use dynamic programming. The brute-force recursive solution tries all possible horizontal and vertical first cuts, recursively cutting the two pieces optimally. By caching (memoizing) the value of the function for each input, we get a polynomial-time dynamic program (O(m n max(m, n))).

    0 讨论(0)
  • 2020-12-28 18:15

    I'd write this as a dynamic (recursive) program.

    Write a function which tries to split the rectangle at some position. Call the function recursively for both parts. Try all possible splits and take the one with the minimum result.

    The base case would be when both sides are equal, i.e. the input is already a square, in which case the result is 1.

    function min_squares(m, n):
    
        // base case:
        if m == n: return 1
    
        // minimum number of squares if you split vertically:
        min_ver := min { min_squares(m, i) + min_squares(m, n-i)  |  i ∈ [1, n/2] }
    
        // minimum number of squares if you split horizontally:
        min_hor := min { min_squares(i, n) + min_squares(m-i, n)  |  i ∈ [1, m/2] }
    
        return min { min_hor, min_ver }
    

    To improve performance, you can cache the recursive results:

    function min_squares(m, n):
    
        // base case:
        if m == n: return 1
    
        // check if we already cached this
        if cache contains (m, n):
            return cache(m, n)
    
        // minimum number of squares if you split vertically:
        min_ver := min { min_squares(m, i) + min_squares(m, n-i)  |  i ∈ [1, n/2] }
    
        // minimum number of squares if you split horizontally:
        min_hor := min { min_squares(i, n) + min_squares(m-i, n)  |  i ∈ [1, m/2] }
    
        // put in cache and return
        result := min { min_hor, min_ver }
        cache(m, n) := result
        return result
    

    In a concrete C++ implementation, you could use int cache[100][100] for the cache data structure since your input size is limited. Put it as a static local variable, so it will automatically be initialized with zeroes. Then interpret 0 as "not cached" (as it can't be the result of any inputs).

    Possible C++ implementation: http://ideone.com/HbiFOH

    0 讨论(0)
  • 2020-12-28 18:26

    I think both the DP and greedy solutions are not optimal. Here is the counterexample for the DP solution:

    Consider the rectangle of size 13 X 11. DP solution gives 8 as the answer. But the optimal solution has only 6 squares.

    This thread has many counter examples: https://mathoverflow.net/questions/116382/tiling-a-rectangle-with-the-smallest-number-of-squares

    Also, have a look at this for correct solution: http://int-e.eu/~bf3/squares/

    0 讨论(0)
  • 2020-12-28 18:29

    Here is a greedy impl. As @David mentioned it is not optimal and is completely wrong some cases so dynamic approach is the best (with caching).

    def greedy(m, n):
        if m == n:
            return 1
        if m < n:
            m, n = n, m
        cuts = 0
    
        while n:
            cuts += m/n
            m, n = n, m % n
        return cuts
    
    print greedy(2, 7)
    

    Here is DP attempt in python import sys

    def cache(f):
        db = {}
    
        def wrap(*args):
            key = str(args)
            if key not in db:
                db[key] = f(*args)
            return db[key]
        return wrap
    
    
    @cache
    def squares(m, n):
        if m == n:
            return 1
        xcuts = sys.maxint
        ycuts = sys.maxint
        x, y = 1, 1
        while x * 2 <= n:
            xcuts = min(xcuts, squares(m, x) + squares(m, n - x))
            x += 1
        while y * 2 <= m:
            ycuts = min(ycuts, squares(y, n) + squares(m - y, n))
            y += 1
        return min(xcuts, ycuts)
    
    0 讨论(0)
提交回复
热议问题