【问题标题】:Algorithm for finding the maximum number of non-overlapping lines on the x axis查找 x 轴上最大非重叠线数的算法
【发布时间】:2013-06-05 09:33:03
【问题描述】:

我不太确定如何问这个问题,但我会尽量具体。 想象一个俄罗斯方块屏幕,只有不同形状的矩形,落到底部。 我想计算我可以在没有任何重叠的情况下一个相邻的矩形的最大数量。我在标题中将它们命名为线条,因为我实际上只对计算时矩形的长度感兴趣,或者与它下降的 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

我不确定是否有此类问题的类型,所以我不知道如何命名它。我对一种可以应用于此类对象列表并会相应地对它们进行排序的算法感兴趣。

我用 c++ 标记了它,因为我正在编写的代码是 c++,但任何语言都可以用于算法。

【问题讨论】:

  • 我认为你需要一个启发式的方法。为什么不先从最小的部分开始呢?
  • 要明确一点,删除第二个或第三个矩形并不重要,因为它是要最大化的矩形数量,而不是总长度的总和?
  • 您的意思是:“我们可以观察到第三个对象的起始坐标为 4 5。”?
  • @Tom 是的,就是这样。问题是,我需要根据多种因素来决定删除哪个:它们中的任何一个是否会影响其他矩形的放置?哪个更长?等
  • @John 是的,谢谢。我已经编辑过了

标签: c++ algorithm sorting


【解决方案1】:

您基本上是在解决以下问题。假设我们有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(0)=0 开始迭代计算f(i)f(n) 给出最大子集的大小。要获得实际的子集,您需要保留一个单独的数组s(i)=\argmax_{0&lt;=j&lt;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

【讨论】:

  • 很好的解决方案。是什么让您认为可能存在O(n log n) 算法?
  • 在计算生物学中,有一个相关的更复杂的问题,称为对齐链,如果目标函数足够简单,已知它有几个O(nlogn) 解决方案。如果我是对的,我可以使用这些算法来解决 OP 的问题。
  • 我选择了这个作为接受的答案,因为它是最明确的,不仅关于解决方案,而且关于要求。谢谢!
【解决方案2】:

这个问题的名称是装箱,它通常被认为是一个难题,但对于少量的箱可以很好地计算。

这是a video 解释解决此问题的常用方法

编辑:通过难题,我的意思是必须使用某种蛮力。您将不得不评估很多解决方案并拒绝其中的大多数,因此通常您需要某种评估机制。您需要能够比较解决方案,例如“此解决方案包含 4 个面积为 15 的矩形”优于“此解决方案包含 3 个面积为 16 的矩形”。

【讨论】:

  • 错了,不是装箱,是NP完全的,也不是断货问题。如果 user172818 的答案是正确的,则有一个 O(n²) 方法。请注意,在 bin packing 中,项目在 bin 中没有固定位置。 -1
【解决方案3】:

我想不出捷径,所以您可能必须按大小降序枚举幂集并在第一个匹配时停止。

执行此操作的直接方法是枚举减小大小的组合。你可以在 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

【讨论】:

    猜你喜欢
    • 2012-05-12
    • 2013-12-29
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 2014-12-03
    相关资源
    最近更新 更多