zkw费用流
因为某些原因被迫学习
我们回顾普通费用流的做法,通过每次spfa找到最小费用的流.
但是每次只松弛一条路径,效率低下.
于是就有了类似最大流多路增广的算法------zkw费用流
算法1
使用spfa把S到所有点的距离全部算出来
然后满足在最短路上的点一定满足\(dis[u]+length(u,v)=dis[v]\)
根据这个条件,可以写出一个较优秀的算法
bool spfa() {
memset(dis, 127, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[S] = 0; q.push(S);
while (!q.empty()) {
int u = q.front(); q.pop(); in[u] = 0;
for (int i = lst[u]; i; i = g[i].nxt)
if (g[i].w && dis[g[i].to] > dis[u] + g[i].v) {
dis[g[i].to] = dis[u] + g[i].v;
if (!in[g[i].to]) in[g[i].to] = 1, q.push(g[i].to);
}
}
return dis[T] != dis[0];
}
LL ans;
int dfs(int u, int flow) {
if (u == T) {
ans += dis[T] * flow;
return flow;
}
vis[u] = 1; int fl = 0;
for (int &i = cur[u]; i; i = g[i].nxt) {
int v = g[i].to; if (vis[v]) continue;
if (g[i].w && dis[v] == dis[u] + g[i].v) {
int d = dfs(v, min(flow, g[i].w));
g[i].w -= d; g[i ^ 1].w += d; fl += d;
flow -= d; if (!flow) break;
}
}
return fl;
}
void Mcmf(){
while (spfa()) {
for (int i = 1; i <= T; i++)
cur[i] = lst[i];
dfs(S, 2147483647);
}
}
算法2
上述代码虽然效率不错,但是一些毒瘤题或者骗分还是跑不过.
我们发现,算法的瓶颈在于spfa?
一次增广后,\(S->T\)的最短路会发生一些变化(一些边被流满惹)
这时通过再一次跑\(dfs\)可以把\(S\)能到的点找出来,然后被流满的边就是这个点集边缘的那些边.
我们就可以快速更新最短路了.
具体实现是维护\(T\)到其他点的最短路,然后松弛点集中的点.
bool spfa() {
queue<int> que;
for (int i = 0; i <= N; i++) dis[i] = INF;
dis[T] = 0, inq[T] = 1, que.push(T);
while (!que.empty()) {
int x = que.front(); que.pop();
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to, w = e[i ^ 1].cost;
if (e[i ^ 1].cap && dis[v] > dis[x] + w) {
dis[v] = dis[x] + w;
if (!inq[v]) que.push(v), inq[v] = 1;
}
}
inq[x] = 0;
}
return dis[S] != INF;
}
int tim, vis[MAX_N];
bool relabel() {
int res = INF;
for (int x = 0; x <= N; x++) {
if (vis[x] != tim) continue;
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to;
if (e[i].cap && vis[v] != tim) res = min(res, dis[v] + e[i].cost - dis[x]);
}
}
if (res == INF) return 0;
for (int i = 0; i <= N; i++) if (vis[i] == tim) dis[i] += res;
return 1;
}
int dfs(int x, int f) {
if (x == T) return f;
vis[x] = tim;
int res = 0;
for (int &i = cur[x]; ~i; i = e[i].next) {
int v = e[i].to, w = e[i].cost;
if (e[i].cap && dis[x] == dis[v] + w && vis[v] != tim) {
int d = dfs(v, min(f, e[i].cap));
res += d, f -= d;
e[i].cap -= d, e[i ^ 1].cap += d;
if (!f) break;
}
}
return res;
}
void zkw() {
spfa();
int flow = 0, res = 0;
do {
int f = 0;
do {
for (int i = 0; i <= N; i++)
cur[i] = fir[i];
++tim;
f = dfs(S, INF);
flow += f, res += dis[S] * f;
} while (f);
} while (relabel());
printf("%d %d\n", flow, res);
}
代码是蒯的