题目背景

滚粗了的\(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;
}