我试图解决以下问题:
将M * N的矩形纸张切成正方形,使得:
- 沿着与纸张的一面平行的线切割纸张。
- 剪切纸张,使得最终尺寸始终为整数。
醇>当纸张无法进一步切割时,此过程将停止。
切割的纸张最小数量是多少都是正方形?
限制: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++;
但我没有弄清楚这种做法有什么不对,因为它给了我一个错误的答案。
答案 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个方格。
这个帖子有很多反例: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)