測試題目:
題目背景
滾粗了的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
原理:
現在的代碼相對於以前就多了一個now數組;now數組運用在Dinic算法的dfs部分,以前我們每次總是從一個點的第一條邊開始進行,但是因爲我們如果得到了一次答案那麼這次答案的路徑一永遠不會再次去跑一邊的,now數組去記錄了每次跑完之後的的當前的邊,使得當我們的dfs回溯到一個跑過一次的點之後可以直接從now數組記錄的邊開始跑dfs。每次的bfs跑完之後需要初始化一次now數組。
我的代碼 在這道題目從 2.2s左右 優化到了 660ms左右 。
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];
int now[M];
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);
}
}
}
return d[t] != 0;
}
inline int dinic(int u, int flow) {
int i;
if(u == t) return flow;
for(i = now[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;
}
now[u] = i;
}
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()) {
memcpy(now, h, sizeof h);
while(flow = dinic(s, inf)) maxflow += flow;
}
printf("%d\n", maxflow);
// fclose(stdin);
return 0;
}