题目:
题目背景
滚粗了的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
解题思路:
构图:首先虚构一个s,t(源点,汇点),将每一个书拆成2个点,然后让源点指向所有的练习册,让所有的练习册指向对应的一边的书的点,然后一边的书的点指向另一边书的点,接着让第二边书的点指向对应的答案的点,最后让所有答案的点指向汇点。图中所有的边权都为1.
这样子构图就保证了只有一本书同时有一本答案和练习册同时选中才会让maxflow ++,并且边权为1保证了每一个节点只选用一次。
最后跑一遍最大流就OK了。
注意点:
1.数组的范围不能开小了
2.注意点的下标的取值
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 4e4 + 5, M = 2e5;
const int inf = 0x3f3f3f3f;
int n1, n2, n3, m1, m2, s, t, maxflow;
int h[N], w[M], e[M], ne[M], idx;
int d[N];
inline void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
e[idx] = a, w[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}
inline bool bfs(void) {
queue<int> q;
while(q.size()) q.pop();
memset(d, 0, sizeof d);
d[s] = 1, q.push(s);
while(q.size()) {
int u = q.front(); q.pop();
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(w[i] && !d[v]) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
if(d[t] == 0) return false;
return true;
}
inline int dinic(int u, int flow) {
if(u == t) return flow;
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(w[i] && d[v] == d[u] + 1) {
int k = dinic(v, min(flow, w[i]));
if(k) {
w[i] -= k;
w[i ^ 1] += k;
return k;
} else d[v] = 0;
}
}
return 0;
}
int main(void) {
// freopen("in.txt", "r", stdin);
scanf("%d%d%d", &n1, &n2, &n3);
memset(h, -1, sizeof h);
s = n2 + 2 * n1 + n3 + 1, t = s + 1;
//源点连练习册
for(int i = 1; i <= n2; i ++)
add(s, i, 1);
//练习册连书
scanf("%d", &m1);
for(int i = 1; i <= m1; i ++) {
int a, b;
scanf("%d%d", &a, &b);
add(b, n2 + a, 1);
}
//书连书
for(int i = 1; i <= n1; i ++)
add(n2 + i, n2 + n1 + i, 1);
//书连答案
scanf("%d", &m2);
for(int i = 1; i <= m2; i ++) {
int a, b;
scanf("%d%d", &a, &b);
add(n2 + n1 + a, n2 + 2 * n1 + b, 1);
}
//答案连汇点
for(int i = 1; i <= n3; i ++)
add(n2 + 2 * n1 + i, t, 1);
int flow;
while(bfs())
while(flow = dinic(s, inf)) maxflow += flow;
printf("%d\n", maxflow);
// fclose(stdin);
return 0;
}