匈牙利算法-维基百科的方法对于这个例子无效。

8
我正在尝试在C中实现匈牙利算法。
我有一个矩阵:
35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

我现在需要找到覆盖所有零的最小行数(尽可能多地进行分配)。显然,通过检查,这是第1列和第3列以及第1行。

维基百科建议以下方法

  • 第1行有三个零:选择任意一个(我选择第一个)并进行分配
  • 第2行:分配第一个零
  • 第3行:分配第三个零
  • 第4行未分配(因为唯一的零位于已经有分配零的列中)

如果我按照上述方法对我的矩阵进行操作,结果如下:

35   0'  0   0
 0' 30   0   5
55   5   0' 10
 0  45  30  45

在这里,零撇号是指定的零。然后,按照维基百科以下的说明,我标记第4行(未分配的零),第1列(带有未分配的零的列),然后标记第2行(在标记列中有零的行)。
因此,这表明要涵盖所有零所需的最小线数为:
+--------
|
+--------
|

但是这并没有命中 (2, 3) 的零。相关的 C 代码:

for (i = 0; i < M->size; i++) {
    for (j = 0; j < M->size; j++) {
        if (M->values[i][j] == 0) {
            if (assigned_cols[j] == 0) {
                assigned_cols[j] = 1; // We've assigned something in this col
                assigned_rows[i] = 1; // We've assigned something in this row.
                marked_rows[i] = 0;
                total--;
                break; // Go to the next row
            } else {
                marked_cols[j] = 1; // Then there exists a zero in this col in an unassigned row
                mark_col(M, j); // marks all elements in column j
                total++;
            }
        }
    }
}

这段代码选择哪些零是零素数(指定零)。
然后,该代码标记所有在新标记列中具有指定的行:
 for (i = 0; i < M->size; i++) {
    if (marked_cols[i] == 1) {
        for (j = 0; j < M->size; j++) {
        //iterating through rows
            if (M->values[j][i] == 0) {
                // then (j,i) is a zero in a marked col
                // mark the row
                if (marked_rows[j] != 1) {
                    total++;
                    marked_rows[j] = 1;
                }
                break; // no need to continue more
            }
        }
    }
}

但是这个解释(以及维基百科的解释)不能解释我上面的矩阵。为什么?

1个回答

4
维基百科缺乏对于算法的解释,分配任务将在最后一步完成!
第 0 步
35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

步骤1-2:所有行列至少有一个0,因此步骤1不会改变数组。

35  0  0  0
 0 30  0  5
55  5  0 10
 0 45 30 45

第三步
矩阵中所有的零都必须被覆盖,尽可能少地标记行和/或列

- - - -
|   |
|   |
|   |

请注意,目前还没有任何任务完成,您需要覆盖所有的零。您的覆盖区域中有两个未被覆盖的零(2,3)!
现在取一个未被覆盖的最小元素,例如5(取位置(2,4)上的5)。
- 减去所有未被覆盖的元素5。
- 增加所有被两条线交叉的元素5。
- 其余元素保持不变。
因此,该数组:
40  0  5  0
 0 25  0  0
55  0  0  5
 0 40 30 40

现在再次检查最小要求行数:现在您需要4行(等于数组行数n=4的大小,所以我们停止)。

最后一步是分配: 从只有一个零的行开始分配,这肯定会被分配:

40  0  5  _
 0 25  _  0
55  _  0  5
 _ 40 30 40

存在多个赋值操作(我使用 _ 表示赋值)。

更具体地说,我们得到了两个赋值操作:(一个上面已经说明,总成本为5),以及:

40  _  5  0
 0 25  0  _
55  0  _  5
 _ 40 30 40

此外,也需要花费5元!


编辑

根据评论,似乎我没有理解到op所问的确切部分,因此我将回答上述算法的一般描述并保留下面需要翻译的内容。

错误在于这里(由于维基百科的错误描述):

其中0号质数是指定的零。然后,按照维基百科下面的说明,我标记了第4行(未指定的零),第1列(带有未指定的零的列),然后标记了第2行(带有标记列中零的行)。

直到现在完全同意...但是...它还不完整!!!

当正确标记第二行时,您需要进入维基百科的第二步,并再次检查具有零的列,在这种情况下,第3列也应被标记,这也导致第3行被标记(由于新标记的列3中的指定零),然后停止(不应标记其他行或列)!!

因此,总体上标记的列和行:

 +       +
35   0'  0   0
 0' 30   0   5  +
55   5   0' 10  +
 0  45  30  45  +

通过选择标记列和未标记行获得的行:

- - - -
|   |
|   |
|   |

在第一部分中描述的正确方法,能够在下一阶段产生正确的结果(如上所述),是正确的。

mathstackexchange上可以找到一个非常相似的帖子,字面上说的是同样的事情:

找到覆盖所有零的最小行数


2
抱歉,但这不是我要问的。我正在询问“步骤3”,即您如何找出那三行以覆盖所有零。我知道(2,3)没有被覆盖-那是我的错误:P - TerryA
1 1 0 1 0 // 1 1 0 1 0 // 1 1 0 1 0 // 0 1 1 1 0 // 1 1 1 1 1,该算法不起作用。 - gia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接