教辅的组成
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)
输出格式:
输出包含一个正整数,表示最多可能组成完整书册的数目。
输入输出样例
说明
样例说明:
如题,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 }