HDU-1150 Machine Schedule 二分图匹配

该题是经典的二分图匹配的应用,题义是这样的。给定K个工作,有两台机器,A机器有N种工作状态,B机器有M种工作状态,对于每一个工作,既可以被A完成又可以被B完成,求完成所有工作至少需要转换多少次模式。

将A以及B的不同模式作为二分图的两个部分,工作作为边,则该题转化为求解最小顶点覆盖来求解。

最小顶点覆盖:包含二分图的X,Y部的部分顶点的一个集合,使得所有的边至少有一个顶点在该点集内。最小顶点覆盖就等于二分图的最大匹配。

证明如下:

设二分图的最大匹配数为M(下图中M等于2),现假设下面的二分图只有 (A,a)以及(C,c)两条边,我们通过增加边来还原至原本的图。这个增加边的过程是不能够使得二分图的匹配数增加的。我们在(A,a)以及(C,c)中选取两点作为最小覆盖点(注意定义,只能这样选),我们选取A,C。现在我们来推翻我们的假设,我们就要增加一些边,使得没有端点在A,C里面(注意不能增加匹配数),刚开始想很简单对吧,我们添加一条边(D,c),呵呵完成了,没有在A,C中,不过这并不能推翻我们的结论,因为A,C是我随机选取的两个点,我可以改变主意了,选择A,c作为最小覆盖点。(B,d)行吗?不行,这样匹配数会增加1了,(A,b),(B,c)和(C,d)行吗?看起是没有增加匹配数,由于A点必须选,C,c也都必须选啊,几轮错了吗? 不需要注意我们也已经增加了匹配数,有了增广路径了。不信,你可以用程序跑一跑。由此特殊性的列子我们得出这个结论应该是靠谱的。(不会严谨的证明,>.<!!!)

A ----- a

B        b

C ----- c

D    d

得出了结论,代码出来了,注意在0状态是不用统计转换次数的,因为机器本身停留在mode_0

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
int N, M, K, visit[105], marry[105];
int G[105][105];

int path(int u)
{
for (int i = 0; i < M; ++i) {
if (!G[u][i] || visit[i])
continue;
visit[i] = 1;
if (marry[i] == -1 || path(marry[i])) {
marry[i] = u;
return 1;
}
}
return 0;
}

int main()
{
int a, x, y, ans;
while (scanf("%d", &N), N) {
scanf("%d %d", &M, &K);
ans = 0;
memset(G, 0, sizeof (G));
memset(marry, 0xff, sizeof (marry));
for (int i = 0; i < K; ++i) {
scanf("%d %d %d", &a, &x, &y);
G[x][y] = 1;
}
for (int i = 0; i < N; ++i) {
memset(visit, 0, sizeof (visit));
if (path(i)) {
++ans;
}
}
for (int i = 0; i < M; ++i) {
if (marry[i] == 0 && i != 0) {
--ans;
break;
}
}
if (marry[0] != -1)
--ans;
printf("%d\n", ans);
}
return 0;
}



posted @ 2012-03-28 21:08  沐阳  阅读(323)  评论(0编辑  收藏  举报