• BZOJ4405 [wc2016]挑战NPC


    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000 
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    Description

    小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目:
    有n个球,用整数1到n编号。还有m个筐子,用整数1到m编号。
    每个筐子最多能装3个球。
    每个球只能放进特定的筐子中。具体有e个条件,第i个条件用两个整数vi和ui描述,表示编号为vi的球可以放进编号为ui的筐子中。
    每个球都必须放进一个筐子中。如果一个筐子内有不超过1个球,那么我们称这样的筐子为半空的。
    求半空的筐子最多有多少个,以及在最优方案中,每个球分别放在哪个筐子中。
    小N看到题目后瞬间没了思路,站在旁边看热闹的小I嘿嘿一笑:“水题!”
    然后三言两语道出了一个多项式算法。
    小N瞬间就惊呆了,三秒钟后他回过神来一拍桌子:
    “不对!这个问题显然是NP完全问题,你算法肯定有错!”
    小I浅笑:“所以,等我领图灵奖吧!”
    小O只会出题不会做题,所以找到了你——请你对这个问题进行探究,并写一个程序解决此题。
     

    Input

    第一行包含1个正整数T,表示有T组数据。
    对于每组数据,第一行包含3个正整数n,m,e,表示球的个数,筐子的个数和条件的个数。
    接下来e行,每行包含2个整数vi,ui,表示编号为vi的球可以放进编号为ui的筐子。

    Output

     对于每组数据,先输出一行,包含一个整数,表示半空的筐子最多有多少个。

    Sample Input

    1
    4 3 6
    1 1
    2 1
    2 2
    3 2
    3 3
    4 3

    Sample Output

    2

    HINT

     

     对于所有数据,T≤5,1≤n≤3m。保证 1≤vi≤n,1≤ui≤m,且不会出现重复的条件。


    保证至少有一种合法方案,使得每个球都放进了筐子,且每个筐子内球的个数不超过 3。

    M<=100
     
     
    正解:建图+一般图最大匹配(带花树)
    解题报告:
      这道题明明是一道巧妙建图+一般图最大匹配的模板题,出题人强行卖萌居然说是NPC算法...
      我才不会说我学带花树之前也真以为这道题是NPC算法呢...
      显然这道题和一般图的最大匹配有关,但是有一定的限制,所以关键在于如何建图,才能使得求出放球的数量<=1的框的数量。
      先讲一讲各个部分分吧...
     
      算法一:
        测试点1、2范围很小,暴搜即可;
     
      算法二:
        当e=nm的时候即每个球都可以放在任意一个框中,那么答案唯一确定,方案的话只要先每个放一个,再一个一个放满,这样构造即可得到最优;
     
      算法三:
        测试点4说,存在方案使得m个框均为半空,那么即每个框确保可以最多放一个球,直接连边跑二分图匹配即可;
     
      算法四:
        不存在有半空的框的方案,即每一个框都至少有2个球,则把每个框拆成三个跑二分图匹配即可。
     
        最后讲正解辣:把每个框拆成三个,这三个之间互相连边,每个能放入这个框的球再与这三个拆掉的点连边,跑一般图最大匹配,带花树跑完之后得到全图的最大匹配,答案即为$maxMatch-n$。
      这是为什么呢?
      建图之后,经过观察,对于一个框而言:假如不放球,则最大匹配为1;放入一个球时,最大匹配为2;放入两个球时,最大匹配为2;放入三个球时,最大匹配为3。
      容易发现对于每个框而言,不妨设这个框内放的球数为num,那么maxMatch-num即为这个框产生的贡献。这题的关键在于建图,建图好神奇好巧妙啊QAQ。不过我对于带花树的模板还不够熟练,还是要花时间多看一下,加深理解。
      ps:这道题有一个很关键的问题:就是我必须从N for到1!我必须先匹配球!因为我需要保证球先被匹配,如果我先匹配框的话的确可以保证总匹配数不变,但是有一些框就和另一些框匹配了,而球却落单了。这个问题如果不注意的话只有40分... 
     
     
    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 1211;
    const int MAXM = 1000011;
    int N,n,m,E,ecnt,first[MAXN],to[MAXM],next[MAXM],match[MAXN],pre[MAXN],vis[MAXN],Tim,dui[MAXM],head,tail,id[MAXN],Match,father[MAXN],ans;
    inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; }
    inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline int lca(int x,int y){
    	Tim++;
    	while(vis[x]!=Tim) {
    		if(x){
    			x=find(x);
    			if(vis[x]==Tim) return x;
    			vis[x]=Tim;
    			if(match[x]!=0) x=find(pre[match[x]]);//!!!
    			else x=0;
    		}
    		swap(x,y);
    	}
    	return x;
    }
    
    inline void change(int x,int y,int k){
    	while(find(x)!=k) {
    		pre[x]=y; int z=match[x];//!!!
    		if(id[z]==1) { id[z]=0; dui[++tail]=z; }
    		if(find(z)==z) father[z]=k;
    		if(find(x)==x) father[x]=k;
    		y=z; x=pre[y];
    	}
    }
    
    inline bool bfs(int s){
    	for(int i=1;i<=N;i++) id[i]=-1,father[i]=i; head=tail=0;
    	dui[++tail]=s; id[s]=0; int u;
    	while(head<tail) {
    		head++; u=dui[head];
    		for(int i=first[u];i;i=next[i]) {
    			int v=to[i];
    			if(id[v]==-1) {
    				pre[v]=u; id[v]=1;
    				if(!match[v]) {
    					int last,t,now=v;
    					while(now!=0) {
    						t=pre[now];	last=match[t]; 
    						match[t]=now; match[now]=t;
    						now=last;
    					}
    					return true;
    				}
    				id[match[v]]=0; dui[++tail]=match[v];
    			}
    			else if(id[v]==0/*!!!*/ && find(u)!=find(v)) {
    				int g=lca(u,v);
    				change(u,v,g);
    				change(v,u,g);
    			}
    		}
    	}
    	return false;
    }
    
    inline void work(){
    	int T=getint(); int x,y;
    	while(T--) {
    		n=getint(); m=getint(); E=getint(); ecnt=0; memset(first,0,sizeof(first));
    		for(int i=1;i<=E;i++) {
    			x=getint(); y=getint(); x+=3*m;
    			link(x,(y-1)*3+1);
    			link(x,(y-1)*3+2);
    			link(x,(y-1)*3+3);
    		}
    		for(int i=1;i<=m;i++) {//每个框内部连边
    			int base=(i-1)*3+1;
    			link(base,base+1);
    			link(base,base+2);
    			link(base+1,base+2);
    		}		
    		N=n+3*m; memset(match,0,sizeof(match)); memset(pre,0,sizeof(pre));
    		Tim=0; memset(vis,0,sizeof(vis)); Match=0;//!!!
    		for(int i=N;i>=1;i--) if(!match[i] && bfs(i)) Match++;
    		ans=Match-n;
    		printf("%d
    ",ans);
    	}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    Atitti 图像处理 图像混合 图像叠加 blend 原理与实现
    Atitit Gaussian Blur 高斯模糊 的原理and实现and 用途
    Atitit 图像处理 灰度图片 灰度化的原理与实现
    Atitit (Sketch Filter)素描滤镜的实现  图像处理  attilax总结
    Atitit 实现java的linq 以及与stream api的比较
    Atitit attilax在自然语言处理领域的成果
    Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库
    Atitit 图像处理--图像分类 模式识别 肤色检测识别原理 与attilax的实践总结
    Atitit apache 和guava的反射工具
    atitit。企业的价值观 员工第一 vs 客户第一.docx
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6270321.html
Copyright © 2020-2023  润新知