「THUSCH 2017」换桌(zkw费用流)—对几种费用流算法的总结

https://loj.ac/problem/2979

这个题直接建图(O((nm)^2))的边数,考虑对每个环加一些中转点,就变成了(O(n^2m+nm^2)),然后就是跑费用流了。

先放出三个算法的submission:
SPFA后单路增广:https://loj.ac/submission/781683
SPFA后多路增广:https://loj.ac/submission/781676
zkw多路增广:https://loj.ac/submission/781679
在这个题中,只有zkw费用流才能跑过。
upd:
按这篇博客所说:https://www.cnblogs.com/Narh/p/10841141.html
用线段树优化一下[l,r]的连边(感觉聊胜于无),SPFA后多路增广加上当前弧优化也能跑过。

SPFA多路增广是由单路增广得来的,可以理解为把dinic的BFS换成了SPFA,
但多路增广的dfs部分和dinic有点不同,加了不能走重点(防止在环上绕圈圈,dinic不会绕圈),和zkw的增广部分写法一样。

不难发现,SPFA后多路增广复杂度不会劣于单路增广,在图比较密集时,优于单路增广。

zkw多路增广与SPFA多路增广的不同在于:SPFA多路增广每次都重新求最短路,而zkw多路增广借用KM的顶标思想,一点点扩展最短路。
当图比较密集(长度短)时,zkw优于SPFA多路增广,因为SPFA的复杂度还是摆在那里的,而zkw可以很快的找到新的最短路。
但是,当图比较稀疏时,zkw多路增广可能要扩展很多次才能找到一条新的最短路,而SPFA多路增广只用一次就能找到至少一条最短路,这时SPFA就更快了。

回到本题,图显然很密集,并且数据随机,按理说zkw多路增广也应该跑的最快。

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;

const int N = 2e6 + 5;

int n, m, a[305][305], b[305][305], id[305][305];

int fi[N], nt[N], to[N], r[N], w[N], tot = 1;

void link(int x, int y, int z, int z2) {
//	pp("%d %d %d %d
" ,x, y, z, z2);
	nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, w[tot] = z2, fi[x] = tot;
	nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, w[tot] = -z2, fi[y] = tot;
}

int S, T;

int dis[N], bz[N];

const int inf = 1e9;

ll ans, ans2;

int change() {
	int mh = inf;
	fo(i, 1, T) if(bz[i])
		for(int j = fi[i]; j; j = nt[j]) if(!bz[to[j]] && r[j])
			mh = min(mh, dis[to[j]] + w[j] - dis[i]);
	if(mh == inf) return 0;
	fo(i, 1, T) if(bz[i]) dis[i] += mh;
	return 1;
}

int dg(int x, int flow) {
	if(x == T) {
		ans += dis[S] * flow;
		ans2 += flow;
		return flow;
	}
	bz[x] = 1;
	int use = 0;
	for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]] && r[i] && dis[x] == dis[to[i]] + w[i]) {
		int t = dg(to[i], min(flow - use, r[i]));
		use += t, r[i] -= t, r[i ^ 1] += t;
		if(use == flow) return use;
	}
	return use;
} 

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, n) fo(j, 1, m) scanf("%d", &a[i][j]);
	fo(i, 1, n) fo(j, 1, m) scanf("%d", &b[i][j]);
	fo(i, 1, n) fo(j, 1, m) id[i][j] = (i - 1) * m + j;
	S = 3 * n * m + 1, T = S + 1;
	fo(i, 1, n) fo(j, 1, m) {
		link(S, id[i][j], 1, 0);
		fo(k, a[i][j] + 1, b[i][j] + 1) {
			link(id[i][j], 2 * n * m + id[k][j], 1, 2 * abs(i - k));
		}
		link(n * m + id[i][j], T, 1, 0);
		fo(k, 1, m) {
			int x = abs(j - k);
			if(m - x < x) x = m - x;
			link(2 * n * m + id[i][k], n * m + id[i][j], 1 << 30, x);
		}
	}
	int sum = 0;
	do {
		do {
			fo(i, 1, T) bz[i] = 0;
		} while(dg(S, 1 << 30));
	} while(change());
	if(ans2 < n * m) pp("-1
"); else
		pp("%lld
", ans);
}
原文地址:https://www.cnblogs.com/coldchair/p/12640057.html