1. 程式人生 > >教輔的組成(最大流)

教輔的組成(最大流)

可能 格式 註釋 真的 edge struct string open const

教輔的組成

https://www.luogu.org/problemnew/show/P1231

題目背景

滾粗了的HansBug在收拾舊語文書,然而他發現了什麽奇妙的東西。

題目描述

蒟蒻HansBug在一本語文書裏面發現了一本答案,然而他卻明明記得這書應該還包含一份練習題。然而出現在他眼前的書多得數不勝數,其中有書,有答案,有練習冊。已知一個完整的書冊均應該包含且僅包含一本書、一本練習冊和一份答案,然而現在全都亂做了一團。許多書上面的字跡都已經模糊了,然而HansBug還是可以大致判斷這是一本書還是練習冊或答案,並且能夠大致知道一本書和答案以及一本書和練習冊的對應關系(即僅僅知道某書和某答案、某書和某練習冊有可能相對應,除此以外的均不可能對應)。既然如此,HansBug想知道在這樣的情況下,最多可能同時組合成多少個完整的書冊。

輸入輸出格式

輸入格式:

第一行包含三個正整數N1、N2、N3,分別表示書的個數、練習冊的個數和答案的個數。

第二行包含一個正整數M1,表示書和練習冊可能的對應關系個數。

接下來M1行每行包含兩個正整數x、y,表示第x本書和第y本練習冊可能對應。(1<=x<=N1,1<=y<=N2)

第M1+3行包含一個正整數M2,表述書和答案可能的對應關系個數。

接下來M2行每行包含兩個正整數x、y,表示第x本書和第y本答案可能對應。(1<=x<=N1,1<=y<=N3)

輸出格式:

輸出包含一個正整數,表示最多可能組成完整書冊的數目。

輸入輸出樣例

輸入樣例#1:
5 3 4
5
4 3
2 2
5 2
5 1
5 3
5
1 3
3 1
2 2
3 3
4 3
輸出樣例#1:
2

說明

樣例說明:

如題,N1=5,N2=3,N3=4,表示書有5本、練習冊有3本、答案有4本。

M1=5,表示書和練習冊共有5個可能的對應關系,分別為:書4和練習冊3、書2和練習冊2、書5和練習冊2、書5和練習冊1以及書5和練習冊3。

M2=5,表示數和答案共有5個可能的對應關系,分別為:書1和答案3、書3和答案1、書2和答案2、書3和答案3以及書4和答案3。

所以,以上情況的話最多可以同時配成兩個書冊,分別為:書2+練習冊2+答案2、書4+練習冊3+答案3。

數據規模:

技術分享圖片

對於數據點1, 2, 3,M1,M2<= 20

對於數據點4~10,M1,M2 <= 20000

與這題是相同的題型

https://www.cnblogs.com/Fighting-sh/p/9818674.html

技術分享圖片
  1 #include<iostream>
  2 #include<cstring>
  3 #include<string>
  4 #include<cmath>
  5 #include<cstdio>
  6 #include<algorithm>
  7 #include<queue>
  8 #include<vector>
  9 #include<set>
 10 #define maxn 200005
 11 #define MAXN 200005
 12 #define mem(a,b) memset(a,b,sizeof(a))
 13 const int N=200005;
 14 const int M=200005;
 15 const int INF=0x3f3f3f3f;
 16 using namespace std;
 17 int n;
 18 struct Edge{
 19     int v,next;
 20     int cap,flow;
 21 }edge[MAXN*20];//註意這裏要開的夠大。。不然WA在這裏真的想罵人。。問題是還不報RE。。
 22 int cur[MAXN],pre[MAXN],gap[MAXN],path[MAXN],dep[MAXN];
 23 int cnt=0;//實際存儲總邊數
 24 void isap_init()
 25 {
 26     cnt=0;
 27     memset(pre,-1,sizeof(pre));
 28 }
 29 void isap_add(int u,int v,int w)//加邊
 30 {
 31     edge[cnt].v=v;
 32     edge[cnt].cap=w;
 33     edge[cnt].flow=0;
 34     edge[cnt].next=pre[u];
 35     pre[u]=cnt++;
 36 }
 37 void add(int u,int v,int w){
 38     isap_add(u,v,w);
 39     isap_add(v,u,0);
 40 }
 41 bool bfs(int s,int t)//其實這個bfs可以融合到下面的叠代裏,但是好像是時間要長
 42 {
 43     memset(dep,-1,sizeof(dep));
 44     memset(gap,0,sizeof(gap));
 45     gap[0]=1;
 46     dep[t]=0;
 47     queue<int>q;
 48     while(!q.empty())
 49     q.pop();
 50     q.push(t);//從匯點開始反向建層次圖
 51     while(!q.empty())
 52     {
 53         int u=q.front();
 54         q.pop();
 55         for(int i=pre[u];i!=-1;i=edge[i].next)
 56         {
 57             int v=edge[i].v;
 58             if(dep[v]==-1&&edge[i^1].cap>edge[i^1].flow)//註意是從匯點反向bfs,但應該判斷正向弧的余量
 59             {
 60                 dep[v]=dep[u]+1;
 61                 gap[dep[v]]++;
 62                 q.push(v);
 63                 //if(v==sp)//感覺這兩句優化加了一般沒錯,但是有的題可能會錯,所以還是註釋出來,到時候視情況而定
 64                 //break;
 65             }
 66         }
 67     }
 68     return dep[s]!=-1;
 69 }
 70 int isap(int s,int t)
 71 {
 72     if(!bfs(s,t))
 73     return 0;
 74     memcpy(cur,pre,sizeof(pre));
 75     //for(int i=1;i<=n;i++)
 76     //cout<<"cur "<<cur[i]<<endl;
 77     int u=s;
 78     path[u]=-1;
 79     int ans=0;
 80     while(dep[s]<n)//叠代尋找增廣路,n為節點數
 81     {
 82         if(u==t)
 83         {
 84             int f=INF;
 85             for(int i=path[u];i!=-1;i=path[edge[i^1].v])//修改找到的增廣路
 86                 f=min(f,edge[i].cap-edge[i].flow);
 87             for(int i=path[u];i!=-1;i=path[edge[i^1].v])
 88             {
 89                 edge[i].flow+=f;
 90                 edge[i^1].flow-=f;
 91             }
 92             ans+=f;
 93             u=s;
 94             continue;
 95         }
 96         bool flag=false;
 97         int v;
 98         for(int i=cur[u];i!=-1;i=edge[i].next)
 99         {
100             v=edge[i].v;
101             if(dep[v]+1==dep[u]&&edge[i].cap-edge[i].flow)
102             {
103                 cur[u]=path[v]=i;//當前弧優化
104                 flag=true;
105                 break;
106             }
107         }
108         if(flag)
109         {
110             u=v;
111             continue;
112         }
113         int x=n;
114         if(!(--gap[dep[u]]))return ans;//gap優化
115         for(int i=pre[u];i!=-1;i=edge[i].next)
116         {
117             if(edge[i].cap-edge[i].flow&&dep[edge[i].v]<x)
118             {
119                 x=dep[edge[i].v];
120                 cur[u]=i;//常數優化
121             }
122         }
123         dep[u]=x+1;
124         gap[dep[u]]++;
125         if(u!=s)//當前點沒有增廣路則後退一個點
126         u=edge[path[u]^1].v;
127      }
128      return ans;
129 }
130 
131 int main(){
132     int nn,mm,s,t;
133     int F,D;
134     cin>>n>>F>>D;
135     int a,b,c;
136     isap_init();
137     cin>>nn;
138     for(int i=1;i<=nn;i++){
139         cin>>a>>b;
140         add(b,a+F,1);
141     }
142     cin>>mm;
143     for(int i=1;i<=mm;i++){
144         cin>>a>>b;
145         add(a+F+n,F+n+n+b,1);
146     }
147     for(int i=1;i<=F;i++){
148         add(0,i,1);
149     }
150     for(int i=1;i<=D;i++){
151         add(F+n+n+i,F+n+n+D+1,1);
152     }
153     for(int i=1;i<=n;i++){
154         add(F+i,F+n+i,1);
155     }
156     s=0,t=F+n+n+D+1;
157     n=F+n+n+D+2;
158     cout<<isap(s,t)<<endl;
159 }
View Code

教輔的組成(最大流)