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); 
} 

代码是蒯的

posted @ 2020-06-01 20:41  zzy2005  阅读(204)  评论(0编辑  收藏  举报