【问题标题】:How to solve this matrix problem to collect all the seeds?如何解决这个矩阵问题来收集所有的种子?
【发布时间】:2020-06-10 14:58:03
【问题描述】:

问题描述:

仓鼠哈米发现了一栋有种子的建筑物。帮他把它们都收集起来。

建筑物排列成一个矩阵的房间,一些房间里有种子。进入大楼后,哈米只会直线奔跑,一路穿过大楼并收集那条线上的所有种子。该建筑有许多入口,哈米可以随意进入任何行或列。

他想用尽可能少的运行来收集建筑物中的所有种子。

这是建筑物的示例布局:

+---+---+---+---+---+
| * |   | * |   |   |
+---+---+---+---+---+
| * | * |   |   |   |
+---+---+---+---+---+
| * |   |   |   | * |
+---+---+---+---+---+
|   |   |   | * |   |
+---+---+---+---+---+
|   |   |   | * |   |
+---+---+---+---+---+

这栋建筑有 25 房间 (5X5) 但只有 8 rooms 有种子(房间包含“*”)。

我们需要找到收集所有种子的最小运行次数。

我的方法:

我尝试用一​​些Greedy Approach 解决它,这里是:

1) Start with row/column that contains maximum rooms of seeds (For example: column 1 for here).

2) Updating rows/columns (no of rooms that contain seeds).

3) Repeating steps 1 & 2 until all the seeds are collected.

所以,对于这个例子,当我应用我的方法时,

Column 1开头(包含3个房间),

更新后,下一个将是Column 4

这是现在的建筑情况,

+--+---+---+--+---+
|  |   | * |  |   |
+--+---+---+--+---+
|  | * |   |  |   |
+--+---+---+--+---+
|  |   |   |  | * |
+--+---+---+--+---+
|  |   |   |  |   |
+--+---+---+--+---+
|  |   |   |  |   |
+--+---+---+--+---+

现在三个房间都包含种子,并且都在不同的rowscolumns,我需要把它们都拿走,结果我用5 runs 完成了,也就是maximum全部拿走行或所有列始终是完全考虑种子的答案)。无需任何计算,任何人都可以到达这里。包含所有行或列的运行。

但这个问题可以在 4 次运行中解决。

我正在寻找一种方法/算法来找到最小运行次数(我需要运行哪些行和/或列)。

【问题讨论】:

  • 问题说,哈米可以直线跑——行/列。
  • 有时贪婪的方法不是最好的;这是其中之一。您将不得不编写一个算法来尝试 2 组。
  • 也许另一种方法是专注于减少剩余种子的行/列数。通过这样做,它会得到第 4 列(释放两行)、第 1、2 行(每个释放 1 列)和第 3 列(释放 2 列)。我不知道这种方法是否适用于一般情况。
  • 而不是贪婪地接受具有大多数种子的行,您的示例最好通过拒绝 来解决>linefeest 种子(并采取必要的交叉线)。实际上,在您的示例中,这甚至不涉及任何模棱两可的决定,因为只有一个种子的线永远不会比其交叉线更好。 (但在一般情况下,这也不总是给出最佳解决方案。)

标签: c++ algorithm matrix greedy


【解决方案1】:

矩阵可以形成一个二分图,所有的行可以形成左边的顶点,所有的列都在右边。如果一个单元格中有种子,它将在左右之间建立一个边缘。因此,给定的示例将形成以下二分图。

现在我们只需要选择一组顶点,以便每条边的至少一个端点将包含在该顶点集中。在图论中,这被称为顶点覆盖问题。 https://en.wikipedia.org/wiki/Vertex_cover

为了达到最小顶点覆盖,我们需要找到图的最大匹配。 Hopcroft–Karp algorithm 让我们在多项式时间内找到最大匹配(在最坏的情况下 O(n^2.5)。一旦我们找到最大匹配,Kőnig's theorem 帮助我们在 O(n^2) 时间内找到最小顶点覆盖。

工作原理:

M是最大匹配,最初它被设置为空。 从二分图中,我们需要找到顶点不相交最短增广路径P的最大集合。

这里 P = {r1c1, r2c2, r3c5, r4c4}

M = M(对称差)P = {r1c1, r2c2, r3c5, r4c4}

在这个例子中没有更多的扩充路径。这样就达到了最大匹配。

现在使用 Kőnig 定理,我们需要从匹配的 M 中找到最小顶点覆盖。 这里的顶点覆盖将是 {r1, r2, r3, c4}

So the minimum run is 4.

这个过程的总体复杂度是O(E * (V^0.5))Ev是边和顶点集的长度。对于稀疏矩阵,它将在接近线性的时间内运行。

另一个例子:

+---+---+---+---+---+
|   | * | * |   | * |
+---+---+---+---+---+
|   |   |   | * |   |
+---+---+---+---+---+
| * | * |   | * |   |
+---+---+---+---+---+
|   |   |   | * |   |
+---+---+---+---+---+

二分图:

最大匹配 M = {r1c2, r2c4, r3c1}

最小顶点覆盖 = {r1, r3, c4}

So the minimum run is 3.

如果你只对最小游程感兴趣(只有数字,不需要显示细节)那么你不需要实现Kőnig定理,因为最大匹配的大小是最小顶点覆盖集的长度.

【讨论】:

【解决方案2】:

这个问题基本上是二分图中的顶点覆盖。图中的一组节点对应于行。另一组对应于列。边对应种子,每条边的端点对应边对应的种子坐标。

通过Kőnig's theorem,最小覆盖的大小等于最大匹配的大小,可以使用Hopcroft–Karp高效计算。

【讨论】:

    【解决方案3】:

    也许另一种方法是专注于减少剩余种子的行/列数。 您可以像这样通过计算每行/列中的种子数来找到行/列。

      3   1   1   2   1
    +---+---+---+---+---+
    | * |   | * |   |   | 2
    +---+---+---+---+---+
    | * | * |   |   |   | 2
    +---+---+---+---+---+
    | * |   |   |   | * | 2
    +---+---+---+---+---+
    |   |   |   | * |   | 1
    +---+---+---+---+---+
    |   |   |   | * |   | 1
    +---+---+---+---+---+
    

    然后,您将连续包含种子的行的总和除以种子数。这将为您提供此列的分数。像这样对所有行和列执行此操作。

    例如,以第 1 行为例,它与第 1 列和第 3 列共享种子。 第 1 列有 3 个种子,第 3 列有 1 个,即 4 个种子。 将此数字除以行数得到 2,因此第 1 行的得分为 2。

      2   2   2   1   2
    +---+---+---+---+---+
    | * |   | * |   |   | 2
    +---+---+---+---+---+
    | * | * |   |   |   | 2
    +---+---+---+---+---+
    | * |   |   |   | * | 2
    +---+---+---+---+---+
    |   |   |   | * |   | 2
    +---+---+---+---+---+
    |   |   |   | * |   | 2
    +---+---+---+---+---+
    

    然后你取最小数字非零的行/列。 通过这样做,它会得到第 4 列(释放两行,得分 1)。

                  ↓
    +---+---+---+---+---+
    | * |   | * |   |   |
    +---+---+---+---+---+
    | * | * |   |   |   |
    +---+---+---+---+---+
    | * |   |   |   | * |
    +---+---+---+---+---+
    |   |   |   | * |   |
    +---+---+---+---+---+
    |   |   |   | * |   |
    +---+---+---+---+---+
    

    然后是第 1 行、第 2 行(每行释放 1 列,得分 2)和第 3 行(释放 2 列,得分 1),顺序不限。

    +---+---+---+---+---+
    | * |   | * |   |   | ←
    +---+---+---+---+---+
    | * | * |   |   |   | ←
    +---+---+---+---+---+
    | * |   |   |   | * | ←
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    

    我不知道这种方法是否适用于一般情况。

    【讨论】:

    • 你如何找到这样的行/列来减少更多的行/列?复杂性是什么?
    • @ManojBanik 好问题,我的想法是遍历每一行和每一列并查看,但我认为这可能会更有效地完成。我只是试图找到一种在这种情况下会产生最佳结果的算法,因为复杂性不是问题的一部分。如果我弄清楚了,我可能会更新答案。
    • @ManojBanik 更新了答案,你有什么意见吗?
    • 行标题 [3 1 1 2 1] 如何更改为 [2 2 2 1 2]?
    • 在图 2 中添加了示例,希望能更好地解释这个概念
    【解决方案4】:

    要在每次跑步前得分你基本上可以检查:

    • 如果我们丢弃当前的一条线,将清除多少条相反(正交)的线:+1 每条
    • 还有多少平行线要运行:每条-1

    首轮成绩:

     -4  -4  -4 (-2) -4
    +---+---+---+---+---+
    | * |   | * |   |   | -3
    +---+---+---+---+---+
    | * | * |   |   |   | -3
    +---+---+---+---+---+
    | * |   |   |   | * | -3
    +---+---+---+---+---+
    |   |   |   | * |   | -4
    +---+---+---+---+---+
    |   |   |   | * |   | -4
    +---+---+---+---+---+
    
    • 第 1 列将清除 0 行并留下 4 列运行 = -4
    • 第 4 列将清除 2 行并留下 4 个其他列运行 = -2
    • 第 1 行将清除 1 列并保留 4 行其他运行 = -3

    第二轮成绩:

     -3  -3  -3  -4  -3
    +---+---+---+---+---+
    | * |   | * |   |   | (-1)
    +---+---+---+---+---+
    | * | * |   |   |   | (-1)
    +---+---+---+---+---+
    | * |   |   |   | * | (-1)
    +---+---+---+---+---+
    |   |   |   |   |   | -3
    +---+---+---+---+---+
    |   |   |   |   |   | -3
    +---+---+---+---+---+
    

    可以使用动态编程在多项式时间内 (cols*rows) 求解(计算每行和列的第一个分数,然后在消耗一行时仅更新受影响的分数)。

    【讨论】:

    • 我不明白你的技术,请你再解释一下。谢谢。
    猜你喜欢
    • 2021-04-11
    • 1970-01-01
    • 2016-04-16
    • 2020-01-06
    • 2013-11-07
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-03-21
    相关资源
    最近更新 更多