二分图

二分图

定义

给一张无向图,可以把点分成两个不相交的非空集合,并且在同一集合的点之间没有边相连,那么称这张无向图为一个二分图。

二分图的判定

一张无向图是二分图,当且仅当图中不存在奇环。

vector<vector<int>> e;
vector<int> v; // 会把所有的点染成 1 2 两种颜色
bool flag;

void dfs( int x , int color ){
    v[x] = color;
    for( auto y : e[x] ){
        if( v[y] == 0 ) dfs( y , 3 - color );
        else if( v[y] == color ){
            flag = false;
            return ;
        }
    }
}

bool check(){
    flag = true;
    for( int i = 1 ; flag and i <= n ; i ++ ){
        if( v[i] ) continue;
        dfs( i , 1 );
    }
    return flag;
}

二分图最大匹配

“任意两条边都没有公共端点”的边集合被称为一组的匹配。

在二分图中包含边数最多的一组匹配被称为二分图的最大匹配。

对于任意一组匹配 \(S\)\(S\)是一个边集),属于\(S\)的边叫匹配边,匹配边的端点叫匹配点。

如果在二分图中存在连接两个非匹配点的路径\(path\),则称\(path\)\(S\)的增广路。

增广路存在以下性质:

  1. 长度是奇数
  2. 路径上第\(1,3,5,\dots\)是非匹配边,第\(2,4,6,\dots\)是匹配边

所以对于一组匹配,把增广路上的边的状态全部取反,得到新的边集合\(S’\),则\(S'\)也是一组匹配,且匹配变数多一。

所以二分图的一组最大匹配\(S\),当且仅当二分图中不存在\(S\)的增广路。

匈牙利算法

算法流程

  1. 设$S=\emptyset $
  2. 求增广路\(path\),把路径上边的状态取反得到新的匹配\(S'\)
  3. 重复第2步直到没有增广路

代码实现采用深搜,从\(x\)出发寻找增广路,并且还回溯时把状态取反。

\(N\)个点\(M\)条的二分图,复杂度\(O(NM)\)

// luogu P3386
//给定一个二分图,其左侧点n个,右侧点的个数为m,边数为k,求其最大匹配的边数。
//左侧点编号[1,n],右侧点编号[1,m]。
#include <bits/stdc++.h>

using namespace std;

int n, m, k; // 左右两个集合的元素数量
vector<vector<int>> e;
vector<int> p; // 当前右侧集合对于左侧的元素
vector<bool> vis;// 右侧元素是否已经被访问过

bool match(int x) {
    for (auto y: e[x]) {
        if (vis[y]) continue;
        vis[y] = 1;
        if (p[y] == 0 or match(p[y])) { // 暂未匹配或原来匹配的左侧元素可以找到新的匹配
            p[y] = x;
            return true;
        }
    }
    return false;
}

int Hungarian() {
    int cnt = 0;
    p = vector<int>(n + m + 1);
    for (int i = 1; i <= n; i++) { // 枚举左侧元素
        vis = vector<bool>(n + m + 1);
        if (match(i)) cnt++;
    }
    return cnt;
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cin >> n >> m >> k;
    e = vector<vector<int>>(n + m + 1);

    for (int x, y; k; k--) {
        cin >> x >> y;
        y += n; // 为了方便表示把右侧点映射为 [n+1,n+m]
        e[x].push_back(y), e[y].push_back(x);
    }
    cout << Hungarian() << "\n";
    return 0;
}

二分图的最小点覆盖

给一张二分图,求最小点集\(S\),使得图中任意一条边都至少有一个端点属于\(S\)

König定理

二分图最小点覆盖包含的点数等于二分图最大匹配包含的边数。

构造方法

  1. 求出二分图的最大匹配
  2. 从左部每个非匹配点出发,再执行一次 DFS 求增广路的过程(一定会失败),标记访问过的所有点
  3. 取左部未被标记的点、右部被标记的点,就得到了最小点覆盖。

二分图最大独立集

给一张无向图,图的独立集就是任意两点之间没有边相连的点集,包含点数最多的独立集就是图的最大独立集。

任意两点之间都有一条边相连的子图被称作无向图的团,点数最多团就是图的最大团。

对于一般的无向图,最大团、最大独立集是 NP完全问题。

定理

无向图的最大团就是补图的最大独立集

定理

\(G\)\(n\)个点的二分图,\(G\)的最大独立集大小等于\(n\)减去最大匹配数。

对于二分图去掉最小点覆盖,剩余的点就构成了二分图的最大独立集。

posted @ 2023-10-19 14:00  PHarr  阅读(23)  评论(0编辑  收藏  举报