题意:有一群小朋友,其中一部分小朋友互相认识,给出小朋友之间是否认识的关系。首先判断是否能将所有小朋友分成两组,每组中任意两个人都不认识。如果能的话,将两个认识的小朋友分成一组,问最多能分多少组?
思路:首先是对图进行染色,判断是否能将图分成两个部分,如果不能的话,直接输出“No”,否则继续计算。
求分组:求二分图最大匹配数。代码中注释的vis[i]=1这个语句开始的时候我一直没有加,当时想的是对于每一个点进行搜索的时候标记,但是这样会出现比如2的子节点为4,然后link[4]==2的循环。注意每一次搜索的是link[son],而不是son。
对于每次搜索link[son],而不是son,需要特别注意一下。寻找增广路径的过程遵循这样一个规律:对于一条路径,如果它的起始点是未盖点,同时它的最后一个点也是未盖点,那么这条路径就是一条可增广路径。比如说对于1……2——3……4这样的路径,就可以通过修改为1——2……3——4这种方式使配对增加一。而在这个过程中,如果我们检查1的子节点2,这时候如果2是盖点,我们需要的不是对2寻找一个新的配对对象(即使找到了,我们也不能把1和2连接起来,因为2已经在另一个连线中),而是对3寻找一个新的配对对象。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 205
#define M 40005
struct node
{
int son;
int next;
} Edge[M*2];
int head[N],vis[N],q[N],link[N];
int n,m,cnt;
void AddEdge(int x,int y)
{
Edge[cnt].son=y;
Edge[cnt].next=head[x];
head[x]=cnt++;
Edge[cnt].son=x;
Edge[cnt].next=head[y];
head[y]=cnt++;
return ;
}
int bfs(int k)
{
int h,t;
h=t=0;
q[t++]=k;
vis[k]=0;
while(h!=t)
{
int cur=q[h++];
int u=head[cur];
for(int i=u; i!=-1; i=Edge[i].next)
{
int son=Edge[i].son;
if(vis[son]!=-1&&vis[son]!=(vis[cur]+1)%2) return 1;
else if(vis[son]!=-1) continue;
vis[son]=(vis[cur]+1)%2;
q[t++]=son;
}
}
return 0;
}
int dfs(int father)
{
int u=head[father];
vis[father]=1;
for(int i=u; i!=-1; i=Edge[i].next)
{
int son=Edge[i].son;
if(vis[son]) continue;
vis[son]=1;///开始的时候没有加上这个标记。
if(link[son]==-1||dfs(link[son])==1)
{
link[son]=father;
return 1;
}
}
return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
AddEdge(x,y);
}
memset(vis,-1,sizeof(vis));
int flag=0;
for(int i=1; i<=n; i++)
{
if(vis[i]==-1)
{
flag=bfs(i);
if(flag) break;
}
}
if(flag)
{
printf("No\n");
continue;
}
memset(link,-1,sizeof(link));
int count=0;
for(int i=1; i<=n; i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i)) count++;
}
printf("%d\n",count/2);
}
return 0;
}