1. 程式人生 > >二分圖模板題 P1631

二分圖模板題 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;
}