题目大意:
一个幼儿园,有G个女生,B个男孩男生,女生和女生之间相互认识,男生和男生之间相互认识,同时又M组男生和女生认识,现在老师要挑出一部分小朋友做游戏,游戏要求参加游戏的小朋友都相互认识,问老师最多能挑选出多少小朋友做游戏。
思路分析:
女生之间都认识,男生之间都认识,并有M组男生女生之间也认识,这道题也就是求最多有多少人之间相互认识,也就是最多有多少点和点能建立联系,也就是最大团问题。(最大团问题简单说明:团(clique)是图论中的用语。对于给定图G=(V,E)。其中,V={1,…,n}是图G的顶点集,E是图G的边集。图G的团就是一个两两之间有边的顶点集合。如果一个团不被其他任一团所包含,即它不是其他任一团的真子集,则称该团为图G的极大团(maximal clique)。顶点最多的极大团,称之为图G的最大团(maximum clique)。最大团问题的目标就是要找到给定图的最大团。)
那么反过来想,我们可以用图的顶点数减去图中不能相互建立联系的点的数,而图中不能建立联系的点的数目也就是求这个图的补图的最大匹配数,而图的补图也就是女生和女生之间没有联系,男生和男生之间没有联系,而给出的M组男生和女生之间能建立联系的关系也取反,这样就建立了一个二分图,而给出的M组男生和女生之间能建立联系的关系取反也就是说,如果有一个女生三个男生,女生认识男生1号,2号,那么我们二分图建立的关系就是女生和男生3号。这样,补图中我们其实就相当于把本来不能有联系的点建立了联系,求这些联系的最大匹配数,也就是求出了原图中不能建立的联系,那么最终要求的也就是总的顶点数减去补图中求得的匹配数,其实也就是我们所建立的二分图的最大独立集数。
直观的常用结论:
1、最大团点的数量=补图中最大独立集点的数量(本题应用到的)
2、二分图中,最大独立集点的数量+最小覆盖点的数量=整个图点的数量
3、二分图中,最小覆盖点的数量=最大匹配的数量
4、图的染色问题中,最少需要的颜色的数量=最大团点的数量
代码实现:
#include<stdio.h>
#include<string.h>
int top,ans,by[210],visit[210],rela[210][210];
struct Edge{
int v;
Edge *next;
}*head[210],e[40000];//head为邻接表的头结点
void Addedge(int from,int to){
Edge *p=&e[top++];
p->v=to;
p->next=head[from];
head[from]=p;
}
bool dfs(int k){
for(Edge *p=head[k];p;p=p->next){
if(!visit[p->v]){
visit[p->v]=1;
if(by[p->v]==-1||dfs(by[p->v])){
by[p->v]=k;
return true;
}
}
}
return false;
}
void Hungry(int g){
memset(by,-1,sizeof(by));
for(int i=1;i<=g;i++){
memset(visit,0,sizeof(visit));
if(dfs(i))
ans++;
}
}
int main(){
int g,b,m;
int cnt=0;
while(~scanf("%d%d%d",&g,&b,&m)&&(g||b||m)){
memset(rela,0,sizeof(rela));
memset(head,0,sizeof(head));//注意头结点要清0
cnt++;
top=0,ans=0;
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
rela[a][b]=1;
}
for(int i=1;i<=g;i++){//把女生和男生之间的联系取反
for(int j=1;j<=b;j++){
if(!rela[i][j])
Addedge(i,j);
}
}
Hungry(g);
printf("Case %d: %d\n",cnt,g+b-ans);
}
}