標籤:names 注意 nbsp pac 聲明 ast first 二分圖 swap
本文著作權歸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 Input1
4 3 6
1 1
2 1
2 2
3 2
3 3
4 3Sample Output2HINT
對於所有資料,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\n",ans);}}int main(){ work(); return 0;}
BZOJ4405 [wc2016]挑戰NPC