滾粗了的HansBug在收拾舊語文書,然而他發現了什麼奇妙的東西。ios
蒟蒻HansBug在一本語文書裏面發現了一本答案,然而他卻明明記得這書應該還包含一份練習題。然而出如今他眼前的書多得數不勝數,其中有書,有答案,有練習冊。已知一個完整的書冊均應該包含且僅包含一本書、一本練習冊和一份答案,然而如今全都亂作了一團。許多書上面的字跡都已經模糊了,然而HansBug仍是能夠大體判斷這是一本書仍是練習冊或答案,而且可以大體知道一本書和答案以及一本書和練習冊的對應關係(即僅僅知道某書和某答案、某書和某練習冊有可能相對應,除此之外的均不可能對應)。既然如此,HansBug想知道在這樣的狀況下,最多可能同時組合成多少個完整的書冊。網絡
輸入格式:spa
第一行包含三個正整數N一、N二、N3,分別表示書的個數、練習冊的個數和答案的個數。rest
第二行包含一個正整數M1,表示書和練習冊可能的對應關係個數。code
接下來M1行每行包含兩個正整數x、y,表示第x本書和第y本練習冊可能對應。(1<=x<=N1,1<=y<=N2)blog
第M1+3行包含一個正整數M2,表述書和答案可能的對應關係個數。get
接下來M2行每行包含兩個正整數x、y,表示第x本書和第y本答案可能對應。(1<=x<=N1,1<=y<=N3)string
輸出格式:it
輸出包含一個正整數,表示最多可能組成完整書冊的數目。io
樣例說明:
如題,N1=5,N2=3,N3=4,表示書有5本、練習冊有3本、答案有4本。
M1=5,表示書和練習冊共有5個可能的對應關係,分別爲:書4和練習冊三、書2和練習冊二、書5和練習冊二、書5和練習冊1以及書5和練習冊3。
M2=5,表示數和答案共有5個可能的對應關係,分別爲:書1和答案三、書3和答案一、書2和答案二、書3和答案3以及書4和答案3。
因此,以上狀況的話最多能夠同時配成兩個書冊,分別爲:書2+練習冊2+答案二、書4+練習冊3+答案3。
數據規模:
對於數據點1, 2, 3,M1,M2<= 20
對於數據點4~10,M1,M2 <= 20000
相信大佬們一眼就能看出來這是個網絡流最大流的模板題吧。
怎麼作?直接把每個點都連起來跑最大流?
有沒有注意到這個題每一本書只能用一次?這樣的話要怎麼辦?
有人就會說了,這個好辦,把全部的邊的流量都賦成1不就好了嗎?
額額,好像頗有道理的樣子,可是對於這樣一個圖,
1 2 2
2
1 1
1 2
2
1 1
1 2
若是咱們對他跑最大流的話,咱們的結果會是2但實際應該是1,縱使咱們把全部的邊權都賦成1最後的結果仍是2,那麼咱們要怎麼辦呢?這個時候有人就會說了,拆點啊
咱們將中間的點拆成兩個,而後是他們·直接的流量爲1,這樣保證每個點只被使用一次
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 2000001 #define inf 9999999 using namespace std; queue<int>q; int s,e,x,y,m1,m2,ans,tot=1; int to[N],cap[N],cnt[N],lev[N],head[N],nextt[N]; int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } int add(int x,int y,int z) { tot++;to[tot]=y;cap[tot]=z;nextt[tot]=head[x];head[x]=tot; tot++;to[tot]=x;cap[tot]=0,nextt[tot]=head[y];head[y]=tot; } inline bool bfs() { while(!q.empty()) q.pop(); for(int i=s;i<=e;i++) { lev[i]=-1; cnt[i]=head[i]; } q.push(s),lev[s]=0; while(!q.empty()) { int x=q.front();q.pop(); for(int i=head[x];i;i=nextt[i]) { int t=to[i]; if(cap[i]>0&&lev[t]==-1) { lev[t]=lev[x]+1; q.push(t); if(t==e) return true; } } } return false; } int dinic(int x,int flow) { if(x==e) return flow; int delta,rest=0; for(int &i=cnt[x];i;i=nextt[i]) { int t=to[i]; if(cap[i]>0&&lev[t]>lev[x]) { delta=dinic(t,min(cap[i],flow-rest)); if(delta) { cap[i]-=delta; cap[i^1]+=delta; rest+=delta; if(rest==flow) break; } } } if(rest!=flow) lev[x]=-1; return rest; } int main() { int n1=read(),n2=read(),n3=read(); e=n1*2+n2+n3+1; m1=read(); for(int i=1;i<=m1;i++) { x=read(),y=read(); add(n1*2+y,x,1); } m2=read(); for(int i=1;i<=m2;i++) { x=read(),y=read(); add(x+n1,y+n1*2+n2,1); } for(int i=1;i<=n1;i++) add(i,n1+i,1); for(int i=1;i<=n2;i++) add(s,n1*2+i,1); for(int i=1;i<=n3;i++) add(n1*2+n2+i,e,1); while(bfs()) ans+=dinic(s,inf); printf("%d",ans); return 0; }