zkw费用流 学习笔记

分析

(D_i)为从(S)出发到(i)的最短路
最短路算法保证, 算法结束时
对于任意存在弧((i,j))满足(D_i + c_{ij}ge D_j)
且对于每个 (j) 至少存在一个 (i) 使得等号成立 ②
算法结束后, 恰在最短路上的边满足 (D_j = D_i + c_{ij})

在最小费用流的计算中,我们每次沿 (D_j = D_i + c_{ij})的路径增广
增广会让流量减小,会让部分的弧变得没有流量(即暂时不存在了)
是不会破坏①,但可能会破坏②的
这可能使我们找不到每条边都满足 $ D_j = D_i + c_{ij}$ 新的增广路

普通费用流的方法是:每次增广再使用 SPFA等方法重新计算(D)
这无疑是一种浪费

做法

(D_i + c_{ij}ge D_j~Leftrightarrow~D_i-D_j+c_{ij}ge 0~~)
(D_i + c_{ij}= D_j~Leftrightarrow~D_i-D_j+c_{ij}= 0~~)
对于一个顶标(D),我们可以不断的dfs找(D_i-D_j+c_{ij}=0)的增广路经

假设我们当前dfs失败
即使失败还是有一些点能满足(D_i-D_j+c_{ij}=0)
这些点被我们当前dfs到了
我们记这些点的点集为(V)

找到(Delta= minleft{D_i-D_j+c_{ij} ight} ~|~~ i in V, j otin V, flow_{~ij} > 0)
然后我们对(~~forall iin V ,~~D_i^{pi}=D_i-Delta)

条件①②均没有被破坏
证明:
((i,j))可以分成四类,再根据当前dfs失败的条件,有:

[egin{aligned} iin V,j otin V &原来D_i-D_j+c_{ij} ge Delta> 0&新图 D_i^{pi}-D_j+c_{ij}ge 0\ iin V,jin V &原来D_i-D_j+c_{ij} =0&新图 D_i^{pi}-D_j^{pi}+c_{ij}= 0\ i otin V,j otin V &原来D_i-D_j+c_{ij}ge 0&新图 D_i-D_j+c_{ij}ge 0\ i otin V,jin V &原来D_i-D_j+c_{ij} ge 0&新图 D_i-D_j^{pi}+c_{ij}ge Delta\ end{aligned} ]

可以发现第一类弧中一定有至少一条满足
(原来D_i-D_j+c_{ij}=Delta~~~~~~~新图 D_i^{pi}-D_j+c_{ij}=0)
即至少有一条新的边进入了 (D_j = D_i + c_{ij}) 的子图

可以发现一条增广路的流量为 -D[S]

实现

struct ZKW{
	int flow,cost;
	int D[M],V[M];

	int aug(int x,int fl){
		V[x]=1;
		if(x==T) return cost+=-D[S]*fl, flow+=fl, fl;
		int p,y,tp;
		for(p=e(x);p;p=e[p].nxt)
		if(e[p].f && !V[y=e[p].y] && D[x]+e[p].d-D[y]==0)
			if(tp=aug(y,min(fl,e[p].f))) return e[p].f-=tp, e[p^1].f+=tp, tp;
		return 0;
	}

	bool mdf(){
		if(V[T]==1) return 1;
		int i,x,y,z=INF;
		for(i=2;i<=e.te;i++)
			if(e[i].f&&V[x=e[i^1].y]&&!V[y=e[i].y]) z=min(z,D[x]+e[i].d-D[y]);
		if(z==INF) return 0;
		for(i=0;i<=T;i++) if(V[i]) D[i]-=z;
		return 1;
	}

	void solve(int ned){
		flow=0, cost=0;
		memset(D,0,sizeof D);
		do memset(V,0,sizeof V),aug(S,INF); while(mdf());
		if(flow==ned) printf("%d
",cost);
		else puts("impossible");
	}
}zkw;
原文地址:https://www.cnblogs.com/acha/p/6735037.html