网络流_加上了当前弧优化的Dinic算法

测试题目:
题目背景
滚粗了的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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章