zoukankan      html  css  js  c++  java
  • Strategic game(无向?)二分图最小点覆盖(Poj1463,Uva1292)

    原题链接
    此题求二分图的最小点覆盖,数值上等于该二分图的最大匹配。得知此结论可以将图染色,建有向图,然后跑匈牙利/网络流,如下。然而...

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int MAXN=2000+5;
    int q[MAXN],hd1[MAXN],hd2[MAXN];
    int lnk[MAXN];
    bool vis[MAXN],bw[MAXN];
    int n,ft,rr,cnt1,cnt2;
    struct Edge
    {
    	int t,n;
    }e1[MAXN<<1],e2[MAXN<<1];
    
    inline void build(int f,int t)
    {
    	e1[++cnt1]=(Edge){t,hd1[f]};
    	hd1[f]=cnt1;
    }
    
    inline void build2(int f,int t)
    {
    	e2[++cnt2]=(Edge){t,hd2[f]};
    	hd2[f]=cnt2;
    }
    
    void bfs()
    {
    	ft=rr=0;
    	memset(bw,0,sizeof bw);
    	memset(vis,0,sizeof vis);
    	q[rr++]=0;
    	vis[0]=1;
    	bw[0]=1;
    	while(ft<rr)
    	{
    		int u=q[ft++];
    		for(int i=hd1[u];i;i=e1[i].n)
    		{
    			int v=e1[i].t;
    			if(!vis[v])
    			{
    				vis[v]=1;
    				bw[v]=bw[u]^1;
    				q[rr++]=v;
    			}
    		}
    	}
    }
    
    bool match(int u)
    {
    	for(int i=hd2[u];i;i=e2[i].n)
    	{
    		int v=e2[i].t;
    		if(!vis[v])
    		{
    			vis[v]=1;
    			if(lnk[v]==-1||match(lnk[v]))
    			{
    				lnk[v]=u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    
    int main()
    {
    	while(~scanf("%d",&n))
    	{
    		cnt1=cnt2=0;
    		memset(hd1,0,sizeof hd1);
    		memset(e1,0,sizeof e1);
    		memset(e2,0,sizeof e2);
    		memset(hd2,0,sizeof hd2);
    		memset(lnk,-1,sizeof lnk);
    		
    		int from,m,to;
    		for(int i=0;i<n;++i)
    		{
    			scanf("%d:(%d)",&from,&m);
    			for(int i=1;i<=m;++i)
    				scanf("%d",&to),build(from,to),build(to,from);
    		}
    		bfs();
    		for(int k=0;k<n;++k)
    		{
    			if(bw[k])
    			{
    				for(int i=hd1[k];i;i=e1[i].n)
    					build2(k,e1[i].t);
    			}
    		}
    		int ans=0;
    		for(int i=0;i<n;++i)
    		{
    			if(bw[i])
    			{
    				memset(vis,0,sizeof vis);
    				if(match(i))
    					++ans;
    			}
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    然而我看网络上流传的都是另一种做法,直接输出在原无向图的最大匹配除以2,却很少有人证明(可能是各位大佬都认为这太显然了不用证)。仔细思考这个结论还是比较显然的(虽然我还想了一会),这里给出简单的证明,原来匹配一次的边被分别从从左右两个方向匹配了一次,这样每天匹配边就被记录了两次,又因为是求得的是最大匹配数,所以左右两边的匹配都应是最大匹配,故求给定无向图求最大匹配可以直接在原图求最大匹配,答案为该数值除以2

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int MAXN=2000+5;
    int hd[MAXN],lnk[MAXN];
    bool vis[MAXN];
    int n,cnt;
    struct Edge
    {
    	int t,n;
    }e[MAXN<<1];
    
    inline void build(int f,int t)
    {
    	e[++cnt]=(Edge){t,hd[f]};
    	hd[f]=cnt;
    }
    
    bool match(int u)
    {
    	for(int i=hd[u];i;i=e[i].n)
    	{
    		int v=e[i].t;
    		if(!vis[v])
    		{
    			vis[v]=1;
    			if(lnk[v]==-1||match(lnk[v]))
    			{
    				lnk[v]=u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    
    int main()
    {
    	while(~scanf("%d",&n))
    	{
    		cnt=0;
    		memset(hd,0,sizeof hd);
    		memset(e,0,sizeof e);
    		memset(lnk,-1,sizeof lnk);
    		int from,m,to;
    		for(int i=0;i<n;++i)
    		{
    			scanf("%d:(%d)",&from,&m);
    			for(int i=1;i<=m;++i)
    				scanf("%d",&to),build(from,to),build(to,from);
    		}
    		int ans=0;
    		for(int i=0;i<n;++i)
    		{
    			memset(vis,0,sizeof vis);
    			if(match(i))
    				++ans;
    		}
    		printf("%d
    ",ans>>1);
    	}
    	return 0;
    }
    

    还有DP解法,待填。
    11.02UPD 树形DP解法

  • 相关阅读:
    nginx location指令详解
    nginx日志模块、事件模块
    nginx核心模块常用指令
    Thinkphp <= 5.0.10 缓存导致的Getshell
    Thinkphp 3.2.3 bind注入 update/insert
    Thinkphp 3.2.3 where注入 select/delete
    Thinkphp 3.2.3 parseWhere设计缺陷导致的exp注入 (三)
    Thinkphp 3.2.3 parseWhere设计缺陷导致的exp注入 (二)
    Thinkphp 3.2.3 parseWhere设计缺陷导致的exp注入 (一)
    PHP POP 链
  • 原文地址:https://www.cnblogs.com/chwhc/p/7741655.html
Copyright © 2011-2022 走看看