「THUSCH 2017」换桌(zkw费用流)—对几种费用流算法的总结
这个题直接建图\(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("\n")
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\n" ,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\n"); else
pp("%lld\n", ans);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址