以最小平方数切割矩形

use*_*069 16 c++ algorithm

我正在尝试解决以下问题:

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

  1. 沿着与纸张的一侧平行的线切割纸张.
  2. 切割纸张使得所得尺寸总是整数.

当纸张无法进一步切割时,该过程停止.

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

限制: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++;
Run Code Online (Sandbox Code Playgroud)

但我没有弄到这种方法有什么问题,因为它给了我一个错误的答案.

Wil*_*ire 16

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

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

在此输入图像描述

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

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

  • @ManishSharma:问题陈述说,“纸张是沿着与纸张一侧平行的线切割的”这个答案解决了一些与所问问题不同的其他问题。 (3认同)

lee*_*mes 14

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

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

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

要提高性能,可以缓存递归结果:

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
Run Code Online (Sandbox Code Playgroud)

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

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


Dav*_*tat 8

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

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

  • 这种方法不会涵盖将矩形切成整数的所有可能方法。我愿意相信它无论如何都能找到最佳解决方案。当起始形状为矩形时,算法未涵盖的解决方案效率极低。 (2认同)