zoukankan      html  css  js  c++  java
  • 【BZOJ4405】【WC2016】挑战NPC(带花树)

    【BZOJ4405】【WC2016】挑战NPC(带花树)

    题面

    BZOJ
    洛谷
    Uoj

    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

    题解

    考虑一下可以放的球数和对答案的贡献:
    3个球:1
    2个球:1
    1个球:0
    0个球:0
    我们发现就是可以放的球数整除(2)的结果
    所以,把一个篮子拆成三个
    一个球还是一个球
    如果一个球可以放进一个篮子里,证明着球可以与篮子拆分出来的任意一个点匹配
    现在要求的相当于篮子自身能够匹配的最大数目
    如果超过了两个空,那么就可以自身与自身匹配了,从而产生贡献一。

    所以考虑如下构图:
    每个篮子是三个点,点与点之间互相连边
    每个球是一个点,可以向它可以放的篮子拆出来的三个点连边。

    这样的话,因为保证所有球都有匹配,
    所以最大匹配数=球数+有贡献的篮子自身的匹配

    所以最后的答案就是最大匹配数-球数。

    至于如何计算方案,一定优秀增广球,再增广篮子,否则会出现篮子自身优先匹配,然后球没有匹配的情况,到时方案错误(虽然答案也是对的。。。)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 1111
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct Line{int v,next;}e[MAX*MAX];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    queue<int> Q;
    int dfn[MAX],tim;
    int match[MAX],pre[MAX];
    int f[MAX],vis[MAX],n,m,E;
    int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
    int lca(int u,int v)
    {
    	++tim;u=getf(u);v=getf(v);
    	while(dfn[u]!=tim)
    	{
    		dfn[u]=tim;
    		u=getf(pre[match[u]]);
    		if(v)swap(u,v);
    	}
    	return u;
    }
    void Blossom(int x,int y,int w)
    {
    	while(getf(x)!=w)
    	{
    		pre[x]=y,y=match[x];
    		if(vis[y]==2)vis[y]=1,Q.push(y);
    		if(getf(x)==x)f[x]=w;
    		if(getf(y)==y)f[y]=w;
    		x=pre[y];
    	}
    }
    bool Aug(int S)
    {
    	for(int i=1;i<=n+m+m+m;++i)f[i]=i,vis[i]=pre[i]=0;
    	while(!Q.empty())Q.pop();Q.push(S);vis[S]=1;
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].next)
    		{
    			int v=e[i].v;
    			if(getf(u)==getf(v)||vis[v]==2)continue;
    			if(!vis[v])
    			{
    				vis[v]=2;pre[v]=u;
    				if(!match[v])
    				{
    					for(int x=v,lst;x;x=lst)
    						lst=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;
    					return true;
    				}
    				vis[match[v]]=1,Q.push(match[v]);
    			}
    			else
    			{
    				int w=lca(u,v);
    				Blossom(u,v,w);
    				Blossom(v,u,w);
    			}
    		}
    	}
    	return false;
    }
    void init()
    {
    	memset(h,0,sizeof(h));cnt=1;
    	memset(dfn,0,sizeof(dfn));tim=0;
    	memset(match,0,sizeof(match));
    }
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();m=read();E=read();
    		init();int ans=0;
    		for(int i=1;i<=m;++i)
    		{
    			Add(i,i+m);Add(i+m,i);
    			Add(i,i+m+m);Add(i+m+m,i);
    			Add(i+m,i+m+m);Add(i+m+m,i+m);
    		}
    		for(int i=1;i<=E;++i)
    		{
    			int v=read(),u=read();
    			Add(v+m+m+m,u);Add(u,v+m+m+m);
    			Add(v+m+m+m,u+m);Add(u+m,v+m+m+m);
    			Add(v+m+m+m,u+m+m);Add(u+m+m,v+m+m+m);
    		}
    		for(int i=m+m+m+n;i;--i)if(!match[i]&&Aug(i))++ans;
    		printf("%d
    ",ans-n);
    		for(int i=1;i<=n;++i)printf("%d ",(match[i+m+m+m]-1)%m+1);puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Go语言基础之指针
    Go语言基础之流程控制
    Go语言基础之函数
    Go语言基础之map
    Go语言基础之数组切片
    windows 10中使用命令行关掉占用指定端口的程序
    在window 10查看一下指定命令行工具所在的位置
    关闭掉mysql 8和mysql5.7的密码验证插件validate_password
    mysql 添加数据如果数据存在就更新ON DUPLICATE KEY UPDATE和REPLACE INTO
    使用MySQL yum源安装MySQL
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8719429.html
Copyright © 2011-2022 走看看