用于在x轴上找到最大数量的非重叠线的算法

时间:2013-06-04 20:58:06

标签: c++ algorithm sorting

我不确定如何问这个,但我会尝试尽可能具体。 想象一个俄罗斯方块屏幕,只有不同形状的矩形,落到底部。 我想计算一个接近另一个的矩形的最大数量而不重叠。我在标题中将它们命名为行,因为我实际上只对计算时矩形的长度或者与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 ++,但任何语言都可以用于算法。 initial state

final state

3 个答案:

答案 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)=0f(n)给出最大子集的大小。要获取实际的子集,您需要保留一个单独的数组s(i)=\argmax_{0<=j<i}{f(j)+d(i,j)}。然后,您需要回溯以获得“路径”。

这是O(n^2)算法:您需要计算每个f(i)以及每个f(i)您需要i次测试的数量。我认为应该有O(nlogn)算法,但我不太确定。

编辑:Lua中的一个实现:

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