题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1068
题目大意:有人开展了在学生之间浪漫关系的研究。给定学生之间的关系,求两两之间没有浪漫关系的最大人数集合。
很显然,求的是最大独立集,独立集的意思就是说,集合里任意两个顶点都不会属于同一条边。
最大独立集=顶点数-最大匹配,而最大匹配可以转化成最大流或者是用匈牙利算法求出。
对第一组测试样例的解释。
由图可以看出,这7个点的最大匹配数是4,但是,有重复的,如上图,很显然0和4的匹配出现了两次,1和6的匹配出现了两次,故有效的匹配其实等于算出来的最大匹配除以2,所以,这里实际最大匹配数为2,由于最大独立集=顶点数-最大匹配数,即答案为5。
网上很多少其实看了别人题解都没看明白就照着代码敲了一遍,然后自己写博客就开始瞎BB了。说什么必须女配男。。。
下面是基于匈牙利算法的AC代码:
#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn=1005;
vector<int>G[maxn];
int vis[maxn],girl[maxn],n;
bool Find(int x)
{
for(int i=0;i<G[x].size();i++)
{
int next=G[x][i];
if(!vis[next])
{
vis[next]=1;
if(!girl[next]||Find(girl[next]))
{
girl[next]=x;
return true;
}
}
}
return false;
}
int solve()
{
int ans=0;
for(int i=0;i<n;i++)
{
fill(vis,vis+n+1,0);
if(Find(i)) ans++;
}
return n-ans/2;
}
int main()
{
while(~scanf("%d",&n))
{
int from,cnt,to;
for(int i=0;i<=n;i++) G[i].clear();
fill(girl,girl+n+1,0);
for(int i=0;i<n;i++)
{
scanf("%d: (%d) ",&from,&cnt);
while(cnt--)
{
scanf("%d",&to);
G[from].push_back(to);
}
}
printf("%d\n",solve());
}
return 0;
}
当然,转化成网络流也可以搞的。不过没有匈牙利算法那么快
下面是基于网络流的AC代码;
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=5005,inf=1<<30;
struct edge{int to,cap,rev;};
vector<edge>G[maxn];
int vis[maxn],n,m,k;
void add_edge(int from,int to,int cap)
{
G[from].push_back((edge){to,cap,G[to].size()});
G[to].push_back((edge){from,0,G[from].size()-1});
}
int dfs(int v,int t,int f)
{
if(v==t) return f;
vis[v]=true;
for(int i=0;i<G[v].size();i++)
{
edge &e=G[v][i];
if(!vis[e.to]&&e.cap>0)
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int flow=0;
while(true)
{
fill(vis,vis+maxn,0);
int f=dfs(s,t,inf);
flow+=f;
if(f==0) return flow;
}
}
int main()
{
//0~n-1:计算机对应的顶点
//n~n+m-1:任务对应的顶点
while(~scanf("%d",&n))
{
//scanf("%d%d",&n,&m);
int s=n+n;//超级源点
int t=s+1;//超级汇点
for(int i=0;i<maxn;i++) G[i].clear();
for(int i=0;i<n;i++) add_edge(s,i,1);//在源点跟计算机之间连边
for(int i=0;i<n;i++) add_edge(n+i,t,1);//在汇点跟任务之间连边
for(int i=0;i<n;i++)
{
int from,to,cap=1,cnt;
scanf("%d: (%d) ",&from,&cnt);
while(cnt--)
{
scanf("%d",&to);
add_edge(from,n+to,cap);
}
}
printf("%d\n",n-max_flow(s,t)/2);
}
return 0;
}