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
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
跟着AKCqhzdy大佬学了一发带花树(一般图匹配),然后找了一道题练练手
woc这道题真tm神
首先讲做法:把每一个筐拆成三角形,然后把球和能到的筐的三边连起来,跑一边带花树,然后用ans-n
ans是匹配数
再来说说正确性,因为每个球都能匹配啊,然后ans必定大于等于n
然后它还会让筐来匹配,如果一个筐能够匹配,就说明这个筐最多被用了一个
然后用最大匹配保证正确性
woc太强了Orz出题人
代码如下:
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int n,m; struct node { int x,y,next; }a[210000];int len,last[11000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int fa[11000]; int findfa(int x) { if(fa[x]==x)return x; fa[x]=findfa(fa[x]);return fa[x]; } void unit(int x,int y) { int fx=findfa(x),fy=findfa(y); if(fx!=fy)fa[fx]=fy; } int match[11000],pre[11000],mark[11000]; int head,tail,list[11000]; int ts,vis[11000]; int LCA(int x,int y) { ts++; while(x!=0) { x=findfa(x); vis[x]=ts; x=pre[match[x]]; } while(y!=0) { y=findfa(y);if(vis[y]==ts)return y; vis[y]=ts; y=pre[match[y]]; } } void group(int x,int r) { while(x!=r) { int y=match[x],z=pre[y]; if(findfa(z)!=r)pre[z]=y; if(mark[y]==2) list[++tail]=y, mark[y]=1; if(mark[z]==2) list[++tail]=z, mark[z]=1; unit(x,y);unit(y,z); x=z; } } bool findniu(int s) { for(int i=1;i<=n+3*m;i++)fa[i]=i; memset(pre,0,sizeof(pre)); memset(mark,0,sizeof(mark)); head=1;tail=1;list[1]=s;mark[s]=1; while(head<=tail) { int x=list[head];head++; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(match[x]==y)continue; if(findfa(x)==findfa(y))continue; if(mark[y]==2)continue; if(mark[y]==0) { if(match[y]==0) { int p1=y,p2=x; while(p1!=0&&p2!=0) { int tt=match[p2]; match[p1]=p2;match[p2]=p1; p1=tt;p2=pre[p1]; } return true; } else { pre[y]=x; list[++tail]=match[y]; mark[match[y]]=1; mark[y]=2; } } else if(mark[y]==1) { int r=LCA(x,y); if(findfa(x)!=r)pre[x]=y; if(findfa(y)!=r)pre[y]=x; group(x,r);group(y,r); } } } return false; } int main() { int T_T; scanf("%d",&T_T); while(T_T--) { int E,x,y; scanf("%d%d%d",&n,&m,&E); //1~n ball n+1~n+m*3 base len=0;memset(last,0,sizeof(last)); for(int i=1;i<=m;i++) { ins(n+(i-1)*3+1,n+(i-1)*3+2); ins(n+(i-1)*3+2,n+(i-1)*3+1); ins(n+(i-1)*3+2,n+(i-1)*3+3); ins(n+(i-1)*3+3,n+(i-1)*3+2); ins(n+(i-1)*3+3,n+(i-1)*3+1); ins(n+(i-1)*3+1,n+(i-1)*3+3); } while(E--) { scanf("%d%d",&x,&y); ins(x,n+(y-1)*3+1);ins(n+(y-1)*3+1,x); ins(x,n+(y-1)*3+2);ins(n+(y-1)*3+2,x); ins(x,n+(y-1)*3+3);ins(n+(y-1)*3+3,x); } int ans=0; ts=0;memset(vis,0,sizeof(vis)); memset(match,0,sizeof(match)); for(int i=1;i<=n+3*m;i++) if(match[i]==0) if(findniu(i)==true)ans++; printf("%d\n",ans-n); } return 0; }
by_lmy