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
            }
        }
    }
}

但这(和维基百科的解释)对于我上面的矩阵失败了。怎么来的?

4

1 回答 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

步骤 3
矩阵中的所有零必须通过标记尽可能少的行和/或列来覆盖

- - - -
|   |
|   |
|   |

请注意,到目前为止尚未完成任何作业,您需要覆盖all zeros. 您的封面没有覆盖零(2,3)!

现在取未覆盖的最小元素,例如 5(取位置 (2,4) 的 5)

- 减少(减少 5)所有未涵盖的元素。
- 将所有被两条线交叉的元素增加(5 倍)。
-Rest 保持不变
所以数组:

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 要求的确切部分,所以我将回答这个特定部分,保留上述算法的一般描述。

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

其中零素数是分配的零。然后,按照下面维基百科的说明,我标记第 4 行(未分配的零)、第 1 列(未分配零的列),然后是第 2 行(标记的列中的零行)。

到现在为止完全同意,但是......它不完整!

正确标记第 2 行时,您需要转到第 2 步(维基百科)并再次检查是否有零列,在这种情况下第 3 列也应该被标记,这也会导致第 3 行也被标记(由于在新标记的第 3 列)然后你就停下来(不应标记其他行或列)!!

所以总体上标记的列和行:

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

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

- - - -
|   |
|   |
|   |

这是答案第一部分中描述的正确结果,并导致下一阶段的正确结果(也在上面解释过)。

可以在mathstackexchange上找到一篇非常相似的帖子,说明字面意思相同:

在分配问题中找到覆盖所有零的最小行数

于 2017-10-18T08:28:43.620 回答