我有一个矩形的维度区域:n*m
。我还有一个较小的维矩形:x*y
。覆盖较大矩形的所有区域所需的最小矩形数量是多少?
没有必要打包较小的矩形。允许它们相互重叠,如果需要,可以跨越较大矩形的边界。唯一的要求是我们必须使用最少数量的x*y
矩形。
另一件事是我们可以根据需要旋转较小的矩形(我的意思是90度旋转),以最小化数字。
n,m,x和y:都是自然数。 x,y不必是n,m的因子。
我无法在给定的时间内解决它,我也无法找到方法。我通过采用不同的n个案例开始,m可以被x,y整除。
更新
示例测试用例:
答案 0 :(得分:3)
(更新:请参阅下面的新版本。)
我认为(但我目前没有证据证明)可以丢弃不规则的倾斜,找到最佳解决方案意味着找到切换方块的方向。
你从这样的基本网格开始:
并且最佳解决方案将采用以下两种形式之一:
因此,对于每个点,您可以计算两个选项所需的切片数量:
这是一个非常基本的实现。结果中的“水平”和“垂直”值是非旋转区域中的瓦片数量(图像中以粉红色表示)。
该算法可能会检查两次,并可以使用一些记忆来加快速度。
(测试显示您需要在切换x和y参数的情况下再次运行算法,并且检查两种类型的解决方案确实是必要的。)
function rectangleCover(n, m, x, y, rotated) {
var width = Math.ceil(n / x), height = Math.ceil(m / y);
var cover = {num: width * height, rot: !!rotated, h: width, v: height, type: 1};
for (var i = 0; i <= width; i++) {
for (var j = 0; j <= height; j++) {
var rect = i * j;
var top = simpleCover(n, m - y * j, y, x);
var side = simpleCover(n - x * i, y * j, y, x);
var total = rect + side + top;
if (total < cover.num) {
cover = {num: total, rot: !!rotated, h: i, v: j, type: 1};
}
var top = simpleCover(x * i, m - y * j, y, x);
var side = simpleCover(n - x * i, m, y, x);
var total = rect + side + top;
if (total < cover.num) {
cover = {num: total, rot: !!rotated, h: i, v: j, type: 2};
}
}
}
if (!rotated && n != m && x != y) {
var c = rectangleCover(n, m, y, x, true);
if (c.num < cover.num) cover = c;
}
return cover;
function simpleCover(n, m, x, y) {
return (n > 0 && m > 0) ? Math.ceil(n / x) * Math.ceil(m / y) : 0;
}
}
document.write(JSON.stringify(rectangleCover(3, 3, 2, 2)) + "<br>");
document.write(JSON.stringify(rectangleCover(5, 6, 3, 2)) + "<br>");
document.write(JSON.stringify(rectangleCover(22, 18, 5, 3)) + "<br>");
document.write(JSON.stringify(rectangleCover(1000, 1000, 11, 17)));
这是反例Evgeny Kluev提供的:(68,68,9,8),它返回68,而只有65个矩形的解决方案,如图所示:
更新:改进算法
反例展示了算法概括的方法:从4个角落开始,尝试所有独特的方向组合,以及区域之间边界a,b,c和d的每个位置;如果在中间未覆盖矩形,请尝试两个方向来覆盖它:
以下是这个想法的简单,未经优化的实现;它可能多次检查一些配置,11×17/1000×1000测试需要6.5秒,但它找到了反例和上一版本的其他测试的正确解决方案,因此逻辑似乎是合理的。
这是代码中使用的五个旋转和区域的编号。如果大矩形是正方形,则仅检查前3个旋转;如果小矩形是正方形,则仅检查第一次旋转。 X [i]和Y [i]是区域i中的矩形的大小,并且w [i]和h [i]是以矩形的数量表示的区域i的宽度和高度。
function rectangleCover(n, m, x, y) {
var X = [[x,x,x,y],[x,x,y,y],[x,y,x,y],[x,y,y,x],[x,y,y,y]];
var Y = [[y,y,y,x],[y,y,x,x],[y,x,y,x],[y,x,x,y],[y,x,x,x]];
var rotations = x == y ? 1 : n == m ? 3 : 5;
var minimum = Math.ceil((n * m) / (x * y));
var cover = simpleCover(n, m, x, y);
for (var r = 0; r < rotations; r++) {
for (var w0 = 0; w0 <= Math.ceil(n / X[r][0]); w0++) {
var w1 = Math.ceil((n - w0 * X[r][0]) / X[r][1]);
if (w1 < 0) w1 = 0;
for (var h0 = 0; h0 <= Math.ceil(m / Y[r][0]); h0++) {
var h3 = Math.ceil((m - h0 * Y[r][0]) / Y[r][3]);
if (h3 < 0) h3 = 0;
for (var w2 = 0; w2 <= Math.ceil(n / X[r][2]); w2++) {
var w3 = Math.ceil((n - w2 * X[r][2]) / X[r][3]);
if (w3 < 0) w3 = 0;
for (var h2 = 0; h2 <= Math.ceil(m / Y[r][2]); h2++) {
var h1 = Math.ceil((m - h2 * Y[r][2]) / Y[r][1]);
if (h1 < 0) h1 = 0;
var total = w0 * h0 + w1 * h1 + w2 * h2 + w3 * h3;
var X4 = w3 * X[r][3] - w0 * X[r][0];
var Y4 = h0 * Y[r][0] - h1 * Y[r][1];
if (X4 * Y4 > 0) {
total += simpleCover(Math.abs(X4), Math.abs(Y4), x, y);
}
if (total == minimum) return minimum;
if (total < cover) cover = total;
}
}
}
}
}
return cover;
function simpleCover(n, m, x, y) {
return Math.min(Math.ceil(n / x) * Math.ceil(m / y),
Math.ceil(n / y) * Math.ceil(m / x));
}
}
document.write("(3, 3, 2, 2) → " + rectangleCover(3, 3, 2, 2) + "<br>");
document.write("(5, 6, 3, 2) → " + rectangleCover(5, 6, 3, 2) + "<br>");
document.write("(22, 18, 5, 3) → " + rectangleCover(22, 18, 5, 3) + "<br>");
document.write("(68, 68, 8, 9) → " + rectangleCover(68, 68, 8, 9) + "<br>");