染色法(判定二分圖) + 匈牙利算法(求二分圖的最大匹配)

染色法(判定二分圖) + 匈牙利算法(求二分圖的最大匹配)

1、染色法(判定二分圖)

給定一個n個點m條邊的無向圖,圖中可能存在重邊和自環。

請你判斷這個圖是否是二分圖。

輸入格式
第一行包含兩個整數n和m。

接下來m行,每行包含兩個整數u和v,表示點u和點v之間存在一條邊。

輸出格式
如果給定圖是二分圖,則輸出“Yes”,否則輸出“No”。

數據範圍
1≤n,m≤105
輸入樣例:
4 4
1 3
1 4
2 3
2 4
輸出樣例:
Yes


分析:

二分圖:可以把一個點集中的點分爲兩個子集,所有邊均在兩個子集之間,子集內部沒有邊。

<=>圖中無奇數環<=>二分圖。

color120染色法判定二分圖:\\數組color標記每個點的顏色,共兩種顏色,分別是1號色與2號色,0表示未染色。

i1j2j1接着我們先任選圖中的某點i開始染1號色,同時將與之相連的點j染成2號色。\\如果存在某個點j已經染成了1號色,說明存在衝突,就不是二分圖。

注意:

123(31=232=1)①、1號色與2號色的翻轉可以通過被3減來轉換(3-1=2,3-2=1)。

hTLE②、鄰接表存圖,記得初始化表頭數組h,否則會TLE。

代碼;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1e5+10,M=2*N;

int n,m,e[M],ne[M],h[N],color[N],idx;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool dfs(int u,int c)
{
    color[u]=c;
    
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!color[j])
            if(!dfs(j,3-c)) return false;
        if(color[j]==c) return false;
    }
    
    return true;
}

int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b),add(b,a);
    }
    
    bool flag=true;
    for(int i=1;i<=n;i++)
        if(!color[i])
            if(!dfs(i,1))
            {
                flag=false;
                break;
            }
            
    if(flag) puts("Yes");
    else puts("No");
    
    return 0;
}

2、匈牙利算法(求二分圖的最大匹配)

給定一個二分圖,其中左半部包含n1個點(編號1~ n1),右半部包含n2個點(編號1~n2),二分圖共包含m條邊。

數據保證任意一條邊的兩個端點都不可能在同一部分中。

請你求出二分圖的最大匹配數。

二分圖的匹配:給定一個二分圖G,在G的一個子圖M中,M的邊集{E}中的任意兩條邊都不依附於同一個頂點,則稱M是一個匹配。

二分圖的最大匹配:所有匹配中包含邊數最多的一組匹配被稱爲二分圖的最大匹配,其邊數即爲最大匹配數。

輸入格式
第一行包含三個整數 n1、 n2 和 m。

接下來m行,每行包含兩個整數u和v,表示左半部點集中的點u和右半部點集中的點v之間存在一條邊。

輸出格式
輸出一個整數,表示二分圖的最大匹配數。

數據範圍
1≤n1,n2≤500,
1≤u≤n1,
1≤v≤n2,
1≤m≤105

輸入樣例:
2 2 4
1 1
1 2
2 1
2 2
輸出樣例:
2

分析:

二分圖的最大匹配即二分圖中一對一匹配的點對的數量。

具體步驟:

(ABCB)ADADCBADDEEF...從前到後先直接匹配,若出現衝突(假設A與B已匹配好,C要與B匹配),就查看A能否與其他點D匹配,\\若能,就將A改爲與D匹配,C與B匹配。若不能,則匹配失敗。\\以上過程是一個遞歸的過程,即若A能與D匹配,但是D也已經與E匹配,那麼E再看看能否與F匹配...

示例:

假設最初還未配對時,各點之間的關係如下:
AD,EBE,FCD鄰接表:\\A-D,E\\B-E,F\\C-D
在這裏插入圖片描述
:ADBE接着,從上到下:\\A先與D配對,成功。B直接先與E配對,成功。對應兩條紅線。
在這裏插入圖片描述
CDAAAEAEEBBBFFBFAECD當給C進行配對時,發生了衝突,對應上圖的綠線,D已經與A配對。\\此時我們再查看A能否與其他點配對,A與E也是有連接關係的,A再去與E配對,但是E也已經與B配對好了,\\再重複以上操作,看B能否與其他點配對,發現B與F是有連接關係的,並且F還未配對,此時直接將B與F配對。\\這時A就能和E配對了,C也能和D配對了。

在這裏插入圖片描述
3形象地,我們可將上述過程視作一次相親大會,3對男生和女生。

ADEBEFCD男生A對女生D和E有好感,男生B對女生E和F有好感,而男生C相對癡情專一,只想和女生D在一起。

ADADBEBECDAEBEFFBFAEBFCD3()按照順序,\\首先男生A發現女生D對他也有好感,於是A與D暫時牽手成功。\\接着男生B發現女生E對他也有好感,於是B與E暫時牽手成功。\\最後男生C急了,他執意要追求女生D,此時男生A沒有辦法,他只能忍痛放手,但是他決定去追女生E。\\這時男生B也沒有辦法,只好與E分手,但他還喜歡女生F,所幸女生F還單身,於是B與F牽手成功。\\最後A追到了E,B追到了F,C追到了D。結果相親大會成功撮合了3對(完美)。

代碼:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N=510,M=1e5+10;

int n1,n2,m;
int e[M],ne[M],idx,h[N];
int match[N];
bool st[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool find(int u)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!st[j])
        {
            st[j]=true;
            if(match[j]==0 || find(match[j]))
            {
                match[j]=u;
                return true;
            }
        }
    }
    
    return false;
}

int main()
{
    memset(h,-1,sizeof h);
    cin>>n1>>n2>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    
    int res=0;
    for(int i=1;i<=n1;i++)
    {
        memset(st,false,sizeof st);
        if(find(i)) res++;
    }
    
    cout<<res<<endl;
    
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章