算法{二分图的最小点覆盖集,二分图的最大点独立集}

算法{二分图的最小点覆盖集,二分图的最大点独立集}

@LOC_2

二分图的最小点覆盖集

定义

点覆盖集的特例, 即当图为二分图时;
给定二分图, 从中选择若干个点S,满足对于二分图中的任意边l->r 一定有l \in S || r \in S;
. 比如二分图是l0->r0, l0->r1, 则S可以为{l0}, {r0,r1}, {l0,r0}, ...;

#结论#: |S|的最小值 等于 二分图的最大匹配;
. 证明: @LINK: @LOC_0;

#结论#: 构造S
. 证明: @LINK: @LOC_1;

性质

最小点覆盖集,他是最小权的点覆盖集的特例,即当所有点权值均为1时 此时最小权的点覆盖集 就等于 最小点覆盖集;

@DELI;

点覆盖集的补集 一定是点独立集; (证明参见点覆盖集);

@DELI;

@MARK: @LOC_1;
构造S;
现在还没弄懂, 暂时参考oi-wiki的讲解;

将二分图点集分成左右两个集合,使得所有边的两个端点都不在一个集合。

考虑如下构造:从左侧未匹配的节点出发,按照匈牙利算法中增广路的方式走,即先走一条未匹配边,再走一条匹配边。由于已经求出了最大匹配,所以这样的「增广路」一定以匹配边结束,即增广路是不完整的。在所有经过这样「增广路」的节点上打标记。则最后构造的集合是:所有左侧未打标记的节点和所有右侧打了标记的节点。

首先,这个集合的大小等于最大匹配。左边未打标记的点都一定对应着一个匹配边(否则会以这个点为起点开始标记),右边打了标记的节点一定在一条不完整的增广路上,也会对应一个匹配边。假设存在一条匹配边左侧标记了,右侧没标记,左边的点只能是通过另一条匹配边走过来,此时左边的点有两条匹配边,不符合最大匹配的规定;假设存在一条匹配边左侧没标记,右侧标记了,那就会从右边的点沿着这条匹配边走过来,从而左侧也有标记。因此,每一条匹配的边两侧一定都有标记(在不完整的增广路上)或都没有标记,匹配边的两个节点中必然有一个被选中。

其次,这个集合是一个点覆盖。由于我们的构造方式是:所有左侧未打标记的节点和所有右侧打了标记的节点。假设存在左侧打标记且右侧没打标记的边,对于匹配边,上一段已经说明其不存在,对于非匹配边,右端点一定会由这条非匹配边经过,从而被打上标记。因此,这样的构造能够覆盖所有边。

同时,不存在更小的点覆盖。为了覆盖最大匹配的所有边,至少要有最大匹配边数的点数。

@DELI;

@MARK: @LOC_0;
证明|S| = 最大匹配;
从流网络的角度, 在求完最大流后(最大流等于最大匹配) 你只要证明: 这个最大流P 他可以等效的转换为另一个流T(流量不变) 且T在所有的边上 都有流量 (之前的最大流 他每条边都是整数流量, 现在T流 他会涉及到小数流量);
. P流 其实他就对应了 若干条互不相交的路径 每个路径就是一个匹配, 比如S->l0->r0->T, S->l1->r1->T 每个路径的流量都是1 总路径个数等于总流量等于最大匹配;
对于任意一条边l->r, 我们要使得 他必须有流量经过, 有如下情况:
@IF(这条边在P中有流量): 已经满足条件了;
@ELSE(l->r这条边无流量):
. @IF(l,r在P流里有流量): 即S->l->R0->T, S->L0->r->T, 此时取一个极小值的小数D=1e-100, 从第一个路径里 抽取D流量 让他流到r里 即S->l->R0->T 让一些流量变成S->l->r->T; 但这会导致r->T的流量溢出了D 因此再从第二个路径里 同样抽取D流量 让他流到l里 即S->L0->r->T的一些流量 变成S->l->r->T; 此时 这条边就有流量了;
. @ELIF(l在P流里有流量 但r没有): S->l->R0->T里抽取一些 改成S->l->r->T;
. @ELIF(r在P流里有流量 但l没有): S->L0->r->T里抽取一些 改成S->l->r->T;
. @ELSE: 这是不可能的, 否则你就得到了更大的流;

@DELI;

算法

构造最小点覆盖

//{ ___BipartiteGraph_MinimalPointsCoverage_ (二分图的最小点覆盖,构造方案)
template< class _TG_> void ___BipartiteGraph_MinimalPointsCoverage_( _TG_ const& _g){
    int N = _g.__PointsCount;
    ___Graph_<int> G_flow;
    G_flow.Initialize( N+2, (_g.__EdgesCount + N) * 2, 1);
    int src = N, tar = src + 1;
    for( int a = 0; a < N; ++a){
        if( _g.FirstEdge[a] == -1){ // `a`是二分图的右集合里的;
            G_flow.AddEdge_Undirected( a, tar, {1,0});
            continue;
        }
        G_flow.AddEdge_Undirected( src, a, {1,0});
        for( auto & nex : _g[a]){
            G_flow.AddEdge_Undirected( a, nex.EndPoint, {1,0});
        }
    }
    int const MaxMatch = ___NetworkFlow_MaxFlow( src, tar, G_flow);
    std::vector<int> Match( N, -1); // 任意匹配`l-r` 一定有`Match[r]=l, Match[l]=r`;
    for( int l = 0; l < N; ++l){
        if( _g.FirstEdge[l] == -1){ continue;}
        for( auto it = G_flow[l].begin(); it != G_flow[l].end(); ++it){
            if( it.CurEdgeID & 1){ continue;}
            if( it->EndPoint >= N){ continue;}
            if( it->Weight != 0){ continue;}
            Match[ it->EndPoint] = l;  Match[ l] = it->EndPoint;
        }
    }
    std::vector<int> ANS; // 最小点覆盖
    std::vector<char> Vis( _g.__PointsCount, 0);
    auto Dfs = [&]( auto _dfs, int _r)->void{
        Vis[ _r] = 1;
        auto l = Match[ _r];
        Vis[ l] = 1;
        for( auto & [r,w] : _g[l]){
            if( Vis[r]){ continue;}
            _dfs( _dfs, r);
        }
    };
    for( int l = 0; l < _g.__PointsCount; ++l){
        if( _g.FirstEdge[ l] == -1){ continue;}
        if( Match[l] != -1){ continue;}
        Vis[ l] = 1;
        for( auto const& nex : _g[l]){
            if( Vis[nex.EndPoint] == 0){ Dfs( Dfs, nex.EndPoint);}
        }
    }
    for( int a = 0; a < _g.__PointsCount; ++a){
        if( _g.FirstEdge[ a] == -1){ // `a`是右集合里的;
            if( Match[a] == -1){ continue;}
            if( Vis[a] == 0){ continue;}
        }
        else{
            if( Match[a] == -1){ continue;}
            if( Vis[a]){ continue;}
        }
        ANS.emplace_back( a);
    }
    ASSERTsystem_( (int)ANS.size() == MaxMatch);
}
//} ___BipartiteGraph_MinimalPointsCoverage_

例题

@LINK: https://editor.csdn.net/md/?articleId=128548772;
模板;

二分图的最大点独立集

定义

点独立集的特例, 即当图为二分图时;
给定二分图, 从中选择若干个点S,满足对于二分图中的任意边l->r 一定有!(l \in S && r \in S);
. 比如二分图是l0->r0, l0->r1, 则S可以为{r0,r1}, {l0}, ...;

因此, 一切求最大独立集的问题, 都可以转换为: 求最小点覆盖; 只要求出点覆盖 取个补集 就得到了独立集;

性质

独立集的补集 一定是点覆盖; (证明参见点独立集);

例题

@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=128571164;

@TODO

对于独立集, 再建立一个图G, 如果原二分图中 a,b之间没有边 则连接G: [a-b], 那么G拆点后的二分图 的最大匹配 就是等于 原图的独立集把?

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值