您的位置:首页 > 其它

HDU1150 匈牙利算法(求二分图最大匹配)

2016-01-16 10:30 369 查看
HDU1150

题目大意;有两台机器A和B以及N个需要运行的任务。每台机器有M种不同的模式,而每个任务都恰好在一台机器上运行。如果它在机器A上运行,则机器A需要设置为模式ai,如果它在机器B上运行,则机器A需要设置为模式bi。每台机器上的任务可以按照任意顺序执行,但是每台机器每转换一次模式需要重启一次。请合理为每个任务安排一台机器并合理安排顺序,使得机器重启次数尽量少。

谈匈牙利算法自然避不开Hall定理

Hall定理:对于二分图G,存在一个匹配M,使得X的所有顶点关于M饱和的充要条件是:对于X的任意一个子集A,和A邻接的点集为T(A),恒有: |T(A)| >= |A|

匈牙利算法是基于Hall定理中充分性证明的思想,其基本步骤为:

1.任给初始匹配M;

2.若X已饱和则结束,否则进行第3步;

3.在X中找到一个非饱和顶点x0,

作V1 ← {x0}, V2 ← Φ;

4.若T(V1) = V2则因为无法匹配而停止,否则任选一点y ∈T(V1)\V2;

5.若y已饱和则转6,否则做一条从x0 →y的可增广道路P,M←M⊕E(P),转2;

6.由于y已饱和,所以M中有一条边(y,z),作 V1 ← V1 ∪{z}, V2 ← V2 ∪ {y}, 转4;

二分图的最小顶点覆盖数=最大匹配数

本题就是求最小顶点覆盖数的。

每个任务建立一条边。

最小点覆盖就是求最少的点可以连接到所有的边。本题就是最小点覆盖=最大二分匹配数。

注意一点就是:题目说初始状态为0,所以如果一个任务有一点为0的边不要添加。因为不需要代价

AC代码:

#include<stdio.h>
#include<vector>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
vector < vector <int > >v;
bool used[101],used1[101];
int beg[101];
int n,m,k,a,e,r;
bool bfs(int to)
{
int s=beg[to];
for(int i=0; i<v[s].size(); i++)
{
int point=v[s][i];
if(used1[point]) continue;
used1[point]=true;
if(beg[point]==-1||bfs(point))
{
beg[point]=s;
return true;
}
}
return false;
}
int slove()
{
int num=0;
bool foo=false;
memset(used,false,sizeof(used));
memset(beg,-1,sizeof(beg));
for(int i=0; i<n; i++)
{
for(int j=0; j<v[i].size(); j++)
{
if(beg[v[i][j]]==-1)
{
beg[v[i][j]]=i;
used[i]=true;
num++;
if(i==0)
foo=true;
break;
}
}
}
for(int i=0; i<n; i++)
{
if(!used[i])
{
if(!v[i].empty())
{
memset(used1,false,sizeof(used1));
for(int j=0; j<v[i].size(); j++)
{
int point=v[i][j];
if(used1[point]) continue;
used1[point]=true;
if(beg[point]==-1||bfs(point))
{
beg[point]=i;
num++;
break;
}

}
}
used[i]=true;
}
}
if(beg[0]!=-1)
num--;
if(foo)
return num-1;
else return num;
}
int main()
{
while(scanf("%d",&n)&&n)
{
v.clear();
v.resize(n);
scanf("%d%d",&m,&k);
for(int i=0; i<k; i++)
{
scanf("%d%d%d",&a,&e,&r);
v[e].push_back(r);
}
printf("%d\n",slove());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: