bzoj4405 [wc2016]挑战NPC

bzoj4405 [wc2016]挑战NPC

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

正解:带花树算法。

学了带花树,现在还有点不理解,然后就把这道题作为入门题。。

一道很神奇的一般图匹配问题,不过暴力分挺多的。。

暴力分不多说了:爆搜+模拟构造+二分图匹配+拆点二分图匹配就有$60$分。。

然后就讲正解了:

把每个筐子拆成$3$个筐子,然后两两之间连边。

对于所有的条件,球向筐子的$3$个点连边。

然后跑一般图最大匹配,答案就是匹配数-$n$。

为什么呢?

我们可以注意到,如果一个筐子没有装球,那么匹配数是$1$。

如果一个筐子装了$1$个球,那么匹配数是$2$。

如果一个筐子装了$2$个球,那么匹配数是$2$。

如果一个筐子装了$3$个球,那么匹配数是$3$。

然后我们发现,这样对应起来,答案就是匹配数-$n$。

  1 //It is made by wfj_2048~
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <cstring>
  5 #include <cstdlib>
  6 #include <cstdio>
  7 #include <vector>
  8 #include <cmath>
  9 #include <queue>
 10 #include <stack>
 11 #include <map>
 12 #include <set>
 13 #define inf (1<<30)
 14 #define M (1000010)
 15 #define N (1010)
 16 #define il inline
 17 #define RG register
 18 #define ll long long
 19 #define id(i,j) (3*(i-1)+j)
 20 
 21 using namespace std;
 22 
 23 struct edge{ int nt,to; }g[M];
 24 
 25 int head[N],match[N],pre[N],vis[N],id[N],fa[N],q[M],h,t,n,m,e,mn,num,cnt,ans;
 26 
 27 il int gi(){
 28     RG int x=0,q=1; RG char ch=getchar();
 29     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
 30     if (ch=='-') q=-1,ch=getchar();
 31     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
 32     return q*x;
 33 }
 34 
 35 il void insert(RG int from,RG int to){
 36     g[++num]=(edge){head[from],to},head[from]=num; return;
 37 }
 38 
 39 il int find(RG int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); }
 40 
 41 il int lca(RG int x,RG int y){
 42     ++cnt;
 43     for (x=find(x),y=find(y);vis[x]!=cnt;swap(x,y))
 44     if (x) vis[x]=cnt,x=find(pre[match[x]]);
 45     return x;
 46 }
 47 
 48 il void change(RG int x,RG int y,RG int k){
 49     while (find(x)!=k){
 50     pre[x]=y; RG int z=match[x];
 51     if (id[z]==1) id[z]=0,q[++t]=z;
 52     if (find(z)==z) fa[z]=k;
 53     if (find(x)==x) fa[x]=k;
 54     y=z,x=pre[y];
 55     }
 56     return;
 57 }
 58 
 59 il int bfs(RG int ini){
 60     for (RG int i=1;i<=mn;++i) id[i]=-1,fa[i]=i;
 61     h=t=0,q[++t]=ini,id[ini]=0;
 62     while (h<t){
 63     RG int x=q[++h],v;
 64     for (RG int i=head[x];i;i=g[i].nt){
 65         v=g[i].to;
 66         if (id[v]==-1){
 67         pre[v]=x,id[v]=1;
 68         if (!match[v]){
 69             RG int now=v,t,la;
 70             while (now){
 71             t=pre[now],la=match[t];
 72             match[t]=now,match[now]=t,now=la;
 73             }
 74             return 1;
 75         }
 76         id[match[v]]=0,q[++t]=match[v];
 77         } else if (!id[v] && find(x)!=find(v)){
 78         RG int g=lca(x,v);
 79         change(x,v,g),change(v,x,g);
 80         }
 81     }
 82     }
 83     return 0;
 84 }
 85 
 86 il void work(){
 87     n=gi(),m=gi(),e=gi(),num=0;
 88     memset(head,0,sizeof(head));
 89     for (RG int i=1,u,v;i<=e;++i){
 90     u=gi(),v=gi(),u+=3*m;
 91     insert(u,id(v,1)),insert(id(v,1),u);
 92     insert(u,id(v,2)),insert(id(v,2),u);
 93     insert(u,id(v,3)),insert(id(v,3),u);
 94     }
 95     for (RG int i=1;i<=m;++i){
 96     insert(id(i,1),id(i,2)),insert(id(i,2),id(i,1));
 97     insert(id(i,1),id(i,3)),insert(id(i,3),id(i,1));
 98     insert(id(i,3),id(i,2)),insert(id(i,2),id(i,3));
 99     }
100     mn=3*m+n,cnt=ans=0,memset(match,0,sizeof(match));
101     memset(pre,0,sizeof(pre)),memset(vis,0,sizeof(vis));
102     for (RG int i=mn;i;--i) if (!match[i]) ans+=bfs(i); printf("%d
",ans-n);
103     for (RG int i=3*m+1;i<=mn;++i) printf("%d ",(match[i]-1)/3+1); return;
104 }
105 
106 int main(){
107 #ifndef ONLINE_JUDGE
108     freopen("npc.in","r",stdin);
109     freopen("npc.out","w",stdout);
110 #endif
111     RG int T=gi();
112     while (T--) work();
113     return 0;
114 }