二分图匹配&&最小顶点覆盖

上大佬博客 

https://blog.csdn.net/acdreamers/article/details/8621130

https://blog.csdn.net/qq_41730082/article/details/81456611

https://blog.csdn.net/qq_34921856/article/details/79535004

https://blog.csdn.net/Flynn_curry/article/details/52966283

(1)最小顶点覆盖

最小顶点覆盖要求用最少的点,让每条边都至少和其中一个点关联。即点集里面的点能覆盖所有的边

Knoig定理:二分图的最小顶点覆盖数=二分图的最大匹配数。

算法证明:(真特么难懂)

理解①:首先,最小顶点覆盖一定>=最大匹配,因为假设最大匹配为n,那么我们就得到了n条互不相邻的边,光覆盖这些边就要用到n个点。

这里事实上就可以看出最小顶点覆盖和最大匹配的不同了,最大匹配的点一定是两两成对的,而最小顶点覆盖还有相对孤立的点。注意是相对孤立,并不是他们之间肯定没有边,而是不属于匹配范围内的。

那么匹配范围外的节点,一种就是有边和匹配范围内元素相连但是没有匹配到,一种就是没边

有边的话这个边就连在了匹配范围内,那这个最小顶点覆盖集合就是既可以连接上匹配元素,又可以连接到非匹配元素,相当于这个非匹配范围内的元素被这个集合覆盖

没有边的话就不需要覆盖了啊..因为他没边啊,重新看一眼上面的定义,让每条边都至少和其中一个点关联,既然和边没联系,就不用和他关联了是不是。

至此,匹配范围外的所有节点都不可能影响到最小顶点覆盖数,所以两者完全相等。

理解②:设最小顶点覆盖点集为V

我们先来证明选最大匹配数个点能否把所有的边都覆盖:

当一张二分图达到最大匹配时,左边的点集中未匹配的点肯定不会与右边点集中未匹配的点连接,如果连接,证明含有增广路,可以继续匹配,那么可以得出这么一个结论,达到最大匹配时,左右点集未匹配的点肯定会连接在已匹配的点上

Strategic Game HDU - 1054 

题意:给出n个点的树形图,每一个点可以覆盖与他相连的边,求要覆盖所有边时,最少的顶点数。

思路:树是天然的二分图,看完上面的解析应该就可以做出来了

注意:本题图为双向图,所以结果是最大匹配/2,这种模型一般适用二分图两边的点都一样,否则一般都是建立有向图来匹配的。

#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<utility>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn=2e3+10;
int girl[maxn],head[maxn],vis[maxn];
int n,m,s,t,k;
const int inf=0x3f3f3f3f;
int maxflow=0;
int cnt;
struct node{
	int to,next;
}star[500100];
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
void add(int u,int v){
	
	star[cnt].to=v;
	star[cnt].next=head[u];
	head[u]=cnt++;
	
}
int Find(int idx){
	
	int now;
	
	for(int i=head[idx];i!=-1;i=star[i].next){
		
	    now=star[i].to;//若两人之间有关系且在这次查找中没有被询问 
		
		if(!vis[now]){
			
			vis[now]=1;//标志该男生在此次查找中已被询问 
			
			if(girl[now]==-1||Find(girl[now])){//腾位置,撬墙脚,//询问是否有伴侣或者可被戴绿帽子
				girl[now]=idx;//符合条件则将该女生和该男生匹配上,并标记 
				return 1;
			}
			
		} 
	}
	return 0;
}
int main()
{	
	//ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	while(~scanf("%d",&k)){
		
		cnt=0;
		memset(head,-1,sizeof(head));
		memset(girl,-1,sizeof(girl));//编号从0 开始  所以初始化为-1 
		//cin>>n>>m;
		//scanf("%d %d",&m,&n);
		//m=read();n=read();//女生有多个男生 
		
		int u,v,w;
		
		for(int i=0;i<k;++i){
			scanf("%d:(%d)",&u,&w);
			while(w--){
				scanf("%d",&v);
				add(u,v);add(v,u);
				
			}
			//x=read();
			//cin>>x>>y;
			//y=read();
		}
		
		int ans=0;
		for(int i=0;i<k;++i){
			
			memset(vis,0,sizeof(vis));
			
			if(Find(i))ans++;
			
		}
	   //双向图所以要/2 ,因为会匹配两次,girl[v]=u,girl[u]=v,他们属于同一个匹配
	   printf("%d\n",ans>>1); 
	   最小顶点覆盖 == 最大匹配(双向图)/2;
	   
	   //for(int i=0;i<k;i++)cout<<girl[i]<<endl;
	}
    
   
    
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值