教辅的组成(最大流)

教辅的组成

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

猜你喜欢

转载自www.cnblogs.com/Fighting-sh/p/9822189.html