匈牙利算法 - 维基百科方法不适用于此示例

时间:2017-10-18 05:51:49

标签: c algorithm hungarian-algorithm

我试图在C中实现匈牙利算法。

我有矩阵:

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

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

Wikipedia suggests the following method

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

如果我按照上面的矩阵进行操作,我会得到:

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

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

因此,这意味着命中所有零的最小行是:

+--------
|
+--------
|

但这并没有在(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 个答案:

答案 0 :(得分:5)

维基百科缺乏对算法的解释,分配将在最后一步完成!

第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)未覆盖!!

现在取min未包含的元素,例如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所要求的确切部分所以我将回答这个具体部分,保留上述算法的一般描述。

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

  

其中零素数是指定的零。然后,遵循维基百科   下面的说明,我标记第4行(未分配的零),第1列(col with   未分配的零),然后是第2行(标记为col的行为零)。

直到现在完全同意但是......它还没有完成!!!

  

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

总体而言,标记的列和行:

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

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

- - - -
|   |
|   |
|   |

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

mathstackexchange 上可以找到一个非常相似的帖子: mathstackexchange

finding the minimum number of lines to cover all zeros in an assignment probem