二分图中的各类算法—方案

%%%好强的总结%%%

做了几道题,我也想总结一发二分图的求方案的一些小点子,不过当然不如上面那个人强,所以会忽略掉一些定理正确性的证明,只是讲讲方法。

①最大匹配方案

这个是SB问题吧,直接一点match[i]与i匹配,其中match[i]是左部点,i是右部点。

②最小点覆盖方案

先做一次最大匹配,然后从左部的非匹配点出发,尝试进行增广(一定会失败),并对沿途的节点进行标记。最后取所有左部未标记点,右部标记点即可。

③最大独立集方案

先来区分一下最大独立集和最大独立团,最大独立集是指选出一个点集,使得点集中的任意两点点之间没有边相连;最大独立团是选出一个点集,使点集中的任意两点都有边直接相连。这两个概念是完全相反的,所以有公式:最大独立团=反图中最大独立集。
回到二分图中最大独立集,先说它的大小=总点数-最大匹配数。
这样想,要想它独立且最大=去掉最少的点及其相连的边,使得没有图上边=选出最少的点覆盖所有边。所以它的大小=总点数-最新点覆盖=总点数-最大匹配数。
所以它的方案本质上是与最小点覆盖一致的,也就是说我们应当选的点是非最小点覆盖方案中的点。

④有向无环图的最小路径点覆盖方案

意思是用最少的不相交的路径覆盖所有顶点,或者说每个点只能被覆盖一次。
最小路径点覆盖=n-拆点后最大二分图匹配
建边是这样的,对于一条有向边(x,y),在二分图中建边(x,y+n)。
因为要选出尽量少的路径=选出尽量少的终点=使非匹配点尽量少,所以有上面的公式。
做完后方案也就得到了。注意这样的连边方式,我们其实已经得到了方案。终点不用说,就是失配点。对于一个点x,它的来源点是match[x+n],如果x+n未匹配,则x为起点。所以一条路径就用match可以递归得到。

⑤有向无环图的最小路径可重复点覆盖

意思就是可以重复覆盖一个点。
做法大致相同,只不过要先做一个传递闭包,主要是方便跳过节点,咱们看图说话

二分图中的各类算法—方案_第1张图片

左边使最小路径点覆盖,右边是最小路径可重复点覆盖,区别就在于多了四条黄色的线,这就是传递闭包给我们新连的边,暂且不管弯的那两条。此时再做最小路径点覆盖,那么我们从原来要选3条路径,变成里可以选1-3和2-5-4两条路径,少了1条。发现我们的方案中并没有重复点,这因为我们是按照最小路径点覆盖来做的。巧妙在于传递闭包给我们连边,使得1-5-3路径去掉了一个5的点。也就是说传递闭包是拿来绕点的。

⑥有向无环图的求一个点集,使得两两间无法无法到达

有人说为什么⑤不讲方案,因为⑤的方案一般以⑥的形式呈现。
可以先做一个有向无环图的最小路径可重复点覆盖,因为就是在每条路径上选一个点,所以点数当然等于最小路径可重复点覆盖数。
初始方案为所有终点(即二分图匹配中失配点),然后逐渐往回走(即match[x]),直到所有的点之间允许共存。判断能否共存直接用传递闭包即可。

代码如下,其中li存方案

modify=true;
memset(vis,false,sizeof(vis));
while(modify)
{
    modify=false;
    for(int i=1;i<=ans;i++)
        for(int j=1;j<=n;j++) if(ma[li[i]][j]) vis[j]=true;//点j被其它路径覆盖,不能存在
    for(int i=1;i<=ans;i++)
        if(vis[li[i]])
        {
            modify=true;
            while(vis[li[i]]) li[i]=match[li[i]];//往回走直到可以存在
        }
    for(int i=1;i<=ans;i++) printf("%d ",li[i]);
}

 

你可能感兴趣的:(二分图匹配)