二分圖(染色法、匈牙利算法)


主要內容:
在這裏插入圖片描述

染色法判定二分圖

  • 二分圖:把無向圖分爲兩個集合V1, V2,所有邊都在V1和V2之間,V1或V2內部沒有邊。一個圖是否爲二分圖,一般用"染色法"判斷。
    例如:
    在這裏插入圖片描述
    (1、2在一個集合,3,4在一個集合)

  • 染色法:用兩種顏色(可以賦值爲1、2)對所有頂點進行染色,要求一條邊所連的兩個相鄰頂點的顏色不同,顏色結束後,若所有相鄰頂點的顏色都不相同,就是二分圖。

二分圖一定不是奇數環(如下圖,奇數環存在相鄰兩個點顏色相同的情況,說明它們在一個集合內)
在這裏插入圖片描述

我們可以用DFS來實現:

代碼思路:

  1. 用1、2表示不同顏色,0表示未染色
  2. dfs遍歷所有點。將未染色的點進行染色
  3. 如果當前點未染色,染成另一種顏色,染色失敗返回false,如果當前點染過色但與鄰點撞色,說明兩點在同一集合.
bool dfs(int u, int c)
{
    color[u] = c;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(!color[j])//沒染過色, 染成另外一種顏色
        {
            if(!dfs(j, 3 - c)) return false;//未染成另一種顏色
        }
        else if(color[j] == c) return false;//染過色,但鄰點撞色
    }
    return true;//未發生矛盾
}

在這裏插入圖片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010, M = 200010;//因爲是雙向邊所以m擴大
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
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 != -1; i = ne[i])
    {
        int j = e[i];
        if(!color[j])//沒染過色, 染成另外一種顏色
        {
            if(!dfs(j, 3 - c)) return false;
        }
        else if(color[j] == c) return false;//染過色,但鄰點撞色
    }
    return true;//未發生矛盾
}
int main()
{
    cin >> n >> m;
    memset(h, - 1, sizeof h);//初始化鄰接表

    while(m -- )
    {
        int a, b;
        cin >> 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))//如果染1有矛盾發生
            {
                flag = false;//不是二分圖
                break;
            }
        }

    if(flag) puts("Yes");
    else puts("No");

    return 0;
}

複雜度O(n + m)


匈牙利算法

使用匈牙利算法求二分圖的最大匹配數

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

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

算法分析:

在這裏插入圖片描述
圖中點3條紅線表示匹配成功,匹配的過程分析:

類似於男女匹配,男的先匹配第一次遇見的姑娘,然後往下看另一個男生,如果兩人喜歡同一個姑娘,那這個姑娘如何處理?1號姑娘觀察1號男生還喜歡2號女生,爲了形成一個皆大歡喜的局面,男1女2匹配,男2女1匹配。3號男生雖然也喜歡2號女生,不過人家有對象了,還好他沒吊死在一棵樹上,匹配3號女生。

在這裏插入圖片描述

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510, M = 100010;
int n1, n2, m;
bool st[N];
int h[N], e[M], ne[M], idx;
int match[N];//姑娘對應的男孩
void add(int a, int b)
{//稀疏圖用鄰接表
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool find(int x)
{
    for(int i = h[x]; i != -1; i = ne[i] )//遍歷男生看上的姑娘
    {
        int j = e[i];

        if(!st[j])
        {
            st[j] = true;

            if(match[j] == 0 || find(match[j]))//姑娘未匹配, 或者與姑娘匹配的男生可以選擇另一個妹子
            {
                match[j] = x;//姑娘與x匹配
                return true;//匹配成功
            }
        }
    }
    return false;
}
int main()
{
    cin >> n1 >> n2 >> m;

    memset(h, -1, sizeof h);

    while(m -- )
    {
        int a, b;
        cin >> 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;
}

複雜度分析:

考慮每個男生(共n個),最壞情況下遍歷所有邊(m條),最壞情況下是O(nm),實際情況是並不需要遍歷完所有邊,所以複雜度遠小於O(nm).

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