题目链接
http://poj.org/problem?id=1325
题目大意
k个工作(从0开始编号),a机器n个模式(模式从0开始编号),b机器有m个模式(模式从0开始编号);
每个工作都可以由机器a的一个模式或机器b的一个模式完成且只能由机器a的一个模式或机器b的一个模式完成;
按一定顺序安排完成工作的顺序,使得切换机器的次数最少,求最少的切换机器次数;
最开始机器a和机器b都处于0模式。
解题思路
建图:
将每个工作需要的两个种机器模式连边,那么每一条边就代表一种工作;左集合是一条边的一个端点,右集合是此边的另一个端点。
建好图了就比较好想了,要将所有工作都完成,就意味着要把所有的边全部覆盖;这就是二分图最小点覆盖问题,也就可以转化为二分图最大匹配数。
为什么这么建图?
说实话,我没想到,直接把题意理解错了。
我们可以这么试着理解:
对于一个工作,要么选a机器的模式,要么选b机器的模式,也就是说,对于一个工作而言,不可能既选a机器的模式完成的它,又选b机器的模式完成它,这样必然不是最小点覆盖。类似这种不能同时选的,我们自然想把不能同时选的放到两个不同的集合中,这就是为什么要将一个工作的a机器模式和b机器模式连边。
只要覆盖住了每条边就代表完成了全部的工作。
注意
注意“最开始机器a和机器b处于0模式”,
意味着可以通过a机器的0模式或者b机器的0模式完成的任务,可以在不切换模式的情况下完成,所以不能访问与a或b的0模式相连的边,因为不需要覆盖这条边了,这条边已经被覆盖过了。
至于“最小点覆盖=最大匹配数”的证明,读者自己百度吧,我感觉挺难的,不会。
看代码吧!
AC代码
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=110;
const int M=1005;
vector<int> e[N];
int n,m,k,cnt,vis[N],link[N],job,ma,mb;
bool dfs(int x)
{
for(int i=0;i<e[x].size();i++)
{
int v=e[x][i];
if(vis[v] || v==0) continue;//必须不能访问b机器的0模式
vis[v]=1;
if(link[v]==0 || dfs(link[v]))
{
link[v]=x;
return 1;
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n))
{
if(n==0) break;
scanf("%d%d",&m,&k);
memset(link,0,sizeof link);
cnt=0;
for(int i=0;i<n;i++) e[i].clear();
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&job,&ma,&mb);
e[ma].push_back(mb);//你也可以在这里判断一下,在mb!=0的前提下push_back,这样你就不用在dfs中continue掉v==0的情况了
}
for(int i=1;i<n;i++)//必须是从a机器的1模式开始
{
memset(vis,0,sizeof vis);
if(dfs(i)) cnt++;
}
cout<<cnt<<endl;
}
}总结
我觉得二分图的匈牙利算法还是需要时间沉淀;
自己对二分图的理解不是很透彻导致不会建图,同时对匈牙利算法的理解也不够深刻,导致实现代码的时候会漏掉细节。

京公网安备 11010502036488号