我不确定如何问这个,但我会尝试尽可能具体。 想象一个俄罗斯方块屏幕,只有不同形状的矩形,落到底部。 我想计算一个接近另一个的矩形的最大数量而不重叠。我在标题中将它们命名为行,因为我实际上只对计算时矩形的长度或者与x轴平行的线感兴趣。
所以基本上我有一个带有开始和结束的自定义类型,两个整数都在0到100之间。假设我们有一个从1到n的这些矩形的列表。 rectangle_n.start(除非它是最靠近原点的矩形)必须是> rectangle_(n-1).end,以便它们永远不会重叠。 我正在从一个随机数的文件中读取矩形坐标(都是x轴坐标)。
举个例子: 考虑这个矩形类型对象列表
rectangle_list {start, end} = {{1,2}, {3,5}, {4,7} {9,12}}
我们可以观察到第三个对象的起始坐标为4<前一个矩形的结束坐标为5.因此,在排序此列表时,我必须删除第二个或第三个对象,使它们不重叠。
我不确定是否存在此类问题的类型,所以我不知道如何命名它。我对一个可以应用在这些对象列表上的算法很感兴趣,并会相应地对它们进行排序。
我用c ++标记了这个,因为我写的代码是c ++,但任何语言都可以用于算法。
答案 0 :(得分:5)
您基本上解决了以下问题。假设我们n
间隔为{[x_1,y_1),[x_2,y_2),...,[x_n,y_n)}
x_1<=x_2<=...<=x_n
。我们希望找到这些区间的最大子集,以便子集中的任何区间之间没有重叠。
天真的解决方案是动态编程。它保证找到最佳解决方案。设f(i)
,0<=i<=n
为区间[x_i,y_i)
的最大子集的大小。我们有等式(这是乳胶):
f(i)=\max_{0<=j<i}{f(j)+d(i,j)}
其中d(i,j)=1
当且仅当[x_i,y_i)
和[x_j,y_j)
没有重叠时;否则d(i,j)
为零。您可以从f(i)
开始迭代计算f(0)=0
。 f(n)
给出最大子集的大小。要获取实际的子集,您需要保留一个单独的数组s(i)=\argmax_{0<=j<i}{f(j)+d(i,j)}
。然后,您需要回溯以获得“路径”。
这是O(n^2)
算法:您需要计算每个f(i)
以及每个f(i)
您需要i
次测试的数量。我认为应该有O(nlogn)
算法,但我不太确定。
function find_max(list)
local ret, f, b = {}, {}, {}
f[0], b[0] = 0, 0
table.sort(list, function(a,b) return a[1]<b[1] end)
-- dynamic programming
for i, x in ipairs(list) do
local max, max_j = 0, -1
x = list[i]
for j = 0, i - 1 do
local e = j > 0 and list[j][2] or 0
local score = e <= x[1] and 1 or 0
if f[j] + score > max then
max, max_j = f[j] + score, j
end
end
f[i], b[i] = max, max_j
end
-- backtrack
local max, max_i = 0, -1
for i = 1, #list do
if f[i] > max then -- don't use >= here
max, max_i = f[i], i
end
end
local i, ret = max_i, {}
while true do
table.insert(ret, list[i])
i = b[i]
if i == 0 then break end
end
return ret
end
local l = find_max({{1,2}, {4,7}, {3,5}, {8,11}, {9,12}})
for _, x in ipairs(l) do
print(x[1], x[2])
end
答案 1 :(得分:3)
这个问题的名称是bin pack,它通常被认为是一个难题,但对于少量的垃圾箱可以合理地计算。
以下是a video解释此问题的常见方法
编辑:由于难题,我的意思是必须使用某种蛮力。您将不得不评估许多解决方案并拒绝大部分解决方案,因此通常需要某种评估机制。您需要能够比较解决方案,例如“此解决方案包4个面积为15的矩形”优于“此解决方案包装3个面积为16的矩形”。答案 2 :(得分:2)
我无法想到快捷方式,因此您可能需要按照大小的降序枚举功率设置,并在第一场比赛时停止。
这样做的直接方法是枚举大小减小的组合。你可以在C ++ 11中做这样的事情:
template <typename I>
std::set<Span> find_largest_non_overlapping_subset(I start, I finish) {
std::set<Span> result;
for (size_t n = std::distance(start, finish); n-- && result.empty();) {
enumerate_combinations(start, finish, n, [&](I begin, I end) {
if (!has_overlaps(begin, end)) {
result.insert(begin, end);
return false;
}
return true;
});
}
return result;
}
enumerate_combination
的实施仅作为练习。我假设你已经有has_overlap
。