洛谷 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\)
思路:开始尝试用二分图做这道题的,理论上应该是可以的,但是却WA了,就是建两个二分图,如果这本书与同一本答案和练习都可以匹配,那么最终就可以配。所以就还是用了网络流还做这道题,要涉及到拆点操作,构图思想大致是:源点——练习——书——书的分身(等等再说)——答案——汇点。为什么有两个书呢?因为我们要将书分别连向练习和答案,因此,也必须要用两个书,就复制一遍就好了,建边的时间容量为1,反向边为0。其实就是把分给拆点,目的是限制流量。
代码:
#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
#define maxn 1000007
#define inf 0x3f3f3f3f
using namespace std;
int n1,n2,n3,m1,m2,S,T,head[maxn],num=1,d[maxn];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int v,f,nxt;
}e[1000007];
inline void ct(int u, int v, int f) {
e[++num]=node{v,f,head[u]};
head[u]=num;
}
inline bool bfs() {
memset(d,-1,sizeof(d));
queue<int>q;
q.push(S),d[S]=0;
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(e[i].f&&d[v]==-1) {
d[v]=d[u]+1;
q.push(v);
}
}
}
return d[T]!=-1;
}
int dfs(int u, int f) {
if(u==T) return f;
int rest=0;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(d[v]==d[u]+1&&e[i].f) {
int t=dfs(v,min(e[i].f,f-rest));
if(!t) d[v]=0;
e[i].f-=t;
e[i^1].f+=t;
rest+=t;
if(f==rest) return rest;
}
}
return rest;
}
inline int dinic() {
int ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
int main() {
n1=qread(),n2=qread(),n3=qread();
S=1,T=n1*2+n2+n3+2;
m1=qread();
for(int i=1,u,v;i<=m1;++i) {
u=qread(),v=qread();
ct(v+1,u+n2+1,1);
ct(u+n2+1,v+1,0);
}
for(int i=n2+2;i<=n2+n1+1;++i) ct(i,i+n1,1),ct(i+n1,i,0);
m2=qread();
for(int i=1,u,v;i<=m2;++i) {
u=qread(),v=qread();
ct(n2+u+n1+1,n2+2*n1+v+1,1);
ct(n2+2*n1+v+1,n2+u+n1+1,0);
}
for(int i=2;i<=n2+1;++i) ct(S,i,1),ct(i,S,0);
for(int i=n2+n1*2+2;i<=T-1;++i) ct(i,T,1),ct(T,i,0);
printf("%d\n",dinic());
return 0;
}