二分图知识点总结

二分图知识点

1.最小点覆盖
  • 1.1 定义:最小点覆盖是一个点集,用最少的点和所有边关联

  • 公式: 最小点覆盖数 = 最大匹配数

  • 证明:下图非常关键

    1.我们可以这么构造一组最小点覆盖的点集。 从二分图的右部的每个未匹配点出发,找一条满足未匹配边-匹配边-未匹配边-匹配边……-未匹配边-匹配边的交叉路,并标记路径上的点。完成之后,左部上标记的点和右部上未标记的点共同组成了一个最小点覆盖点集。(图中红色的点为最小点覆盖点集)
    2.首先这样的点集数量一定等于最大匹配数
    图中蓝色边为匹配边,我们可发现匹配边有的在我们找到的交叉路上,有的不在(图中红色箭头的路径),在路径上的匹配边和左部标记的点一一对应,不在路径上的匹配边和右部上未标记的点一一对应,因为左部标记的点为匹配点,右部未标记的点也是匹配的点,他们都连接一条匹配边,根据这种交叉路的定义(未匹配边-匹配边-未匹配边-匹配边)是不存在一条匹配边左端点标记,右端点未标记,因为一条匹配边左端点标记,右端点必定被标记。所以我们选择点集和匹配边一一对应,等于最大匹配数。

    3.其次为什么这个样的点集一定覆盖所有的边
    什么样的边不会被覆盖?左端点未标记,右端点标记的边。 这种情况是不存在的,否则就多出了一条增广路,不符合最大匹配。

    4.为什么是最小的点覆盖?
    废话,覆盖所有的匹配边至少需要最大匹配数的点好吗?
    5.下面是一个可以构造一组最小点覆盖点集的代码

    #include<bits/stdc++.h>
    using namespace std;
    int flag[100];
    int hungray(const vector<vector<int>>G, int m) {
        int n = G.size(), ans = 0;
        vector<int>link(n, -1),use(n, -1);
        memset(flag, 0, sizeof(flag));
        function<int(int, int)>dfs=[&](int u, int d) {
            for(auto& i : G[u]) {
                if(use[i] != d) {
                    use[i] = d;
                    if(link[i] == -1 || dfs(link[i], d)) {
                        link[i] = u; return 1;
                    }
                }
            }
            return 0;
        };
        for(int i = 0; i < m; i++) {
            if(dfs(i,i)) ans++;
        }
        function<void(int ,int)>dfs2=[&](int u, int fa) {
            flag[u] = 1;
            for(auto & i : G[u]) {
                if(i == fa) continue;
                for(auto & j : G[i]) {
                    if(link[j] == i) {
                        flag[i] = 1;
                        dfs2(j, i); break;
                    }
                }
            }
        };
        for(int i = m; i < n; i++) {
            if(link[i] == -1) dfs2(i, -1);
        }
        return ans;
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int n, m, k;
        while(cin >> n && n) {
            cin >> m >> k;
            vector<vector<int>>G(n + m);
            for(int i = 0; i < k; i++) {
                int  x, y; cin  >> x >> y;
                G[x].push_back(y);
                G[y].push_back(x);
            }
            cout << hungray(G, n) << endl;
            for(int i  = 0; i < n + m; i++) {
                if(i < n && flag[i]) cout << i << ' ';
                else if(i >= n && flag[i] == 0) {
                    cout << i << ' ';
                }
            }
            cout << endl;
        }
        return 0;
    }
    
2.最小边覆盖(对于二分图的概念)
  • 2.1 定义:边覆盖是相对于二分图的概念。最小边覆盖是一个边集,对于图中所有的点,至少有一条边与其关联。
  • 2.2 公式:最小边覆盖 = 二分图点数 - 最大匹配数
  • 2.3 证明:若最大匹配为x,能覆盖2*x个点,再加边每次只能多覆盖一个点,否则就能成为匹配边,与已经达到最大匹配不符,那么最小边覆盖,就是总的点数-能覆盖两个点的边(最大匹配数)。
  • 2.4 怎么构造一组解:跑一遍最大匹配,把匹配边都选上,对每个未匹配点选择一条边就完了,代码略。
3.最小路径覆盖(对于DAG的概念)
  • 3.1 最小不相交路径覆盖
    3.1.1 定义:每一条路径经过的顶点各不相同
    3.1.2 算法:把原图的每个点V拆成 V x V_x Vx V y V_y Vy两个点,如果有一条有向边A->B,那么就加边 A x − &gt; B y A_x−&gt;B_y Ax>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。
    3.1.3 证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。
  • 3.2 最小可相交路径覆盖
    3.2.1 定义:每一条路径经过的顶点可以相同
    3.2.2 算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题
    3.2.3 证明:同上3.1.3
    注:特别的每一个点,自己也可以称为路径覆盖,只不过路径长度为0。
4.最大独立集
  • 4.1 定义:独立集是一个点集,任意两个点都之间不可达,最大独立集即点数最大

  • 4.2 公式:二分图最大独立集=最小边覆盖=顶点数-最大匹配数

  • 4.3 证明:和最小点覆盖差不多

  • 4.4 构造一组解:把最小点覆盖的一组解搞出来,然后那几个点不要,剩下的点组成最大独立集。

5.链与反链
  • 链的定义:链是一个点集,在这个集合中任意两个元素要么存在$u \to v , 要 么 存 在 ,要么存在 v\to u$,二者只能存在其一。
  • 反链的定义:反链是一个点集,集合中的任意两点互相无法达到对方。
6.最小链覆盖与最长反链
  • 最小链覆盖的定义:用最少的链,经过所有点最少一次
  • 最长反链的定义:和最大独立集一个意思,选一个集合两两直接不可达
  • 公式:最小链覆盖 = 最长反链
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值