以最小平方数切割矩形

时间:2014-09-18 02:03:52

标签: c++ algorithm

我试图解决以下问题:

  

将M * N的矩形纸张切成正方形,使得:

     
      
  1. 沿着与纸张的一面平行的线切割纸张。
  2.   
  3. 剪切纸张,使得最终尺寸始终为整数。
  4.         

    当纸张无法进一步切割时,此过程将停止。

         

    切割的纸张最小数量是多少都是正方形?

         

    限制:1 <= N <= 100且1 <= M <= 100。

         

    示例:设N = 1且M = 2,则答案为2,因为可切割的最小方格数为2(纸张沿中间较小的一侧水平切割)。

我的代码:

cin >> n >> m;

int N = min(n,m);
int M = max(n,m);
int ans = 0;

while (N != M) {
    ans++;
    int x = M - N;
    int y = N;
    M = max(x, y);
    N = min(x, y);
}

if (N == M && M != 0)
   ans++;

但我没有弄清楚这种做法有什么不对,因为它给了我一个错误的答案。

6 个答案:

答案 0 :(得分:13)

我把它写成一个动态(递归)程序。

编写一个试图在某个位置拆分矩形的函数。以递归方式为两个部分调用该函数。尝试所有可能的拆分,并采取最小结果。

基本情况是两边相等,即输入已经是正方形,在这种情况下结果为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 }

为了提高性能,您可以缓存递归结果:

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

在具体的C ++实现中,您可以使用int cache[100][100]作为缓存数据结构,因为您的输入大小有限。将它作为静态局部变量,因此它将自动用零初始化。然后将0解释为“未缓存”(因为它不能是任何输入的结果)。

可能的C ++实现:http://ideone.com/HbiFOH

答案 1 :(得分:12)

我认为DP和贪婪的解决方案都不是最优的。以下是DP解决方案的反例:

考虑尺寸为13 X 11的矩形.DP解决方案给出8作为答案。但最优解只有6个方格。

enter image description here

这个帖子有很多反例:https://mathoverflow.net/questions/116382/tiling-a-rectangle-with-the-smallest-number-of-squares

另外,请看一下这个正确的解决方案:http://int-e.eu/~bf3/squares/

答案 2 :(得分:8)

贪婪算法不是最佳的。在6x5矩形上,它使用5x5正方形和5个1x1正方形。最优解决方案使用2个3x3正方形和3个2x2正方形。

要获得最佳解决方案,请使用动态编程。蛮力递归解决方案尝试所有可能的水平和垂直第一次切割,以最佳方式递归地切割两个部分。通过缓存(memoizing)每个输入的函数值,我们得到一个多项式时间动态程序(O(m n max(m,n)))。

答案 3 :(得分:1)

使用动态编程可以解决这个问题。

假设我们有一个宽度为N且高度为M的矩形。

  • if (N == M),所以它是一个正方形,无需任何操作。

  • 否则,我们可以将矩形划分为另外两个较小的矩形(N - x,M)和(x,M),因此可以递归求解。

  • 同样,我们也可以将它分为(N,M - x)和(N,x)

伪代码:

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;

答案 4 :(得分:0)

这基本上是经典的整数或0-1背包问题,可以使用贪婪或动态编程方法解决。您可以参考:Solving the Integer Knapsack

答案 5 :(得分:0)

这是一个贪婪的冲动。正如@David所说,它不是最优的,在某些情况下是完全错误的,所以动态方法是最好的(使用缓存)。

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)

这是python中的DP尝试 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)