二分图模板题 P1631
Description
对于无向图 G=(V,E),如果可以把结点集分成不相交的部分,即 X和 Y=V-X,使得每条边的其中一个端点在 X 中,另一个在 Y 中,则称 G 为二分图(bipartite graph)。二分图的另一种等价说法是,可以把每个结点着以黑色和白色之一,使得每条边的两个端点颜色不同,所以黑白染色法这就是判定二分图的基本算法。不难发现,非连通图是二分图当且仅当每个连通分量都是二分图。比如下面的问题就是典型的二分图判定问题:
期末考试的时候,老师要把全班学生分在两个考场。老师在分考场的时候要考虑尽量避免把相互之间比较熟悉的同学分在同一的考场,因为他们可能会联合起来作弊。现在老师统计了同学们相互之间熟悉情况,他想知道能否合理地分考场,将所有相互熟悉的同学分在不同的考场。
Input
输入第一行是一个整数T,表示有T(T<=20)组测试数据。接下来每一组测试数据包括两个部分。第一部分只有一行,有两个整数n,m(2<=n<=1000,m<=n*(n-1)/2)。分别表示学生的人数和老师掌握的学生之间熟悉关系个数。第二部分有m行,每行有两个整数a,b。表示学生a和学生b相互认识,即有可能联合起来作弊。a和b分别表示学生的标号,且从1开始(1<=a,b<=n)。
Output
对于每组测试数据,输出按照样例的格式。第一行表示是第几组数据。如果能把所有学生分在两个考场,且不可能发生作弊行为,第二行输出”Yes”,否则输出”No”。
Hint
T<=202<=n<=1000,m<=n*(n-1)/2
Solution
把每段熟悉关系当做无向图的一条边,为了将每一条边的两个结点都染成不相同的颜色(黑白染色法),这样就能使得所有的熟悉关系都被分在两个不同的组。定义一个vis数组记录dfs过程中(染色过程中)是否遇到了不能染色的情况(eg:1与2和3各有一条边连通,而2和3也有一条边连通,当从1开始dfs时,将1染成黑色,2和3染成白色,那么当dfs到2的时候,3已经进行了染色,所以不是二分图。)需要注意的是这个无向图不一定是全部连通的,那么就需要循环找出是否每个结点都已经染色,如果没有,就要从它又进行dfs,直到所有结点都进行了染色就能知道是否为二分图。
注意事项:
1.存图过程中next存的是一个下标,first和last数组存的也是这条链的下标。在dfs过程中进行循环的时候,循环的下标应该从dfs的这个点的第一条边开始,每次到下一条边直到回到下一个点的第一个下标。
2.由于有多组数据所以要把数组和node清零,edge数组不用清零因为后面一组数据的edge会把前面一组的edge给覆盖。不清零会爆RE。
3.cnt要初始化为1。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 500005
struct Edge{
int u;
int v;
int next;
}edge[maxn];
int node,n,m,x,y,u,cnt;
int first[maxn],last[maxn],color[maxn];
bool vis[maxn];
void ql(){
memset(first,0,sizeof(first));
memset(last,0,sizeof(last));
memset(color,0,sizeof(color));
memset(vis,0,sizeof(vis));
node=0;
}
void addedge(int u,int v){
edge[++node]=(Edge){u,v,0};
if(first[u]==0)first[u]=node;
else edge[last[u]].next=node;
last[u]=node;
return;
}
void init(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
cnt=1;
}
bool dfs(int s,int c){
color[s]=c;
vis[s]=true;
for(int q=first[s];q;q=edge[q].next){
int x=edge[q].v;
if(!vis[x]){
if(!dfs(x,3-c))return false;
}
if(color[x]==color[s])return false;
}
return true;
}
int main(){
scanf("%d",&u);
for(int i=1;i<=u;i++){
ql();
init();
for(int j=1;j<=n;j++){
if(color[j]==0){
cnt=dfs(j,1);
}
if(!cnt)break;
}
printf("case %d:\n",i);
if(cnt)printf("Yes\n");
else{
printf("No\n");
}
}
return 0;
}