[MOBAN]多路增广费用流(ZKW费用流)
以前一直很惊异ZKW是什么,,,结果认真学了之后发现原来就是在原本的SPFA费用流上加了点优化?orz orz orz.
我们都知道SPFA费用流是怎么写的.利用EK+SPFA求出从S到T的一条可行的增广最短路径,然后增广这条路径.但这样在某些情况下,实际上是对这张我们好不容易跑出的最短路是有点浪费.(当然这种情况只要出题人不毒瘤其实出现情况真的可能不会很多).因为有可能有多条从源点到汇点的最短路径.在这种情况下可以一次增广多个容量.
所以解决方法----在跑完SPFA之后,我们不只增广一条路径,而尝试利用DFS去尝试最大流的方法去增广.有点类似dinic,但是其增广依据不是依靠dinic我们BFS出的断层dis,而是判断是否dis[起] + len == dis[终],即判断是否是在起点到终点的最短路径上,这些路径都可以增广.
由于写起来很像dinic,因此dinic的当前弧优化也可以用.
详见代码,很好懂.
code:
int la[maxm],nt[maxm],owo=1,en[maxm],len[maxm],v[maxm],cur[maxm];
void adg(int x,int y,int z,int vv) {
en[++owo]=y; nt[owo]=la[x]; la[x]=owo; len[owo]=z; v[owo]=vv;
en[++owo]=x; nt[owo]=la[y]; la[y]=owo; len[owo]=-z; v[owo]=0;
}
int S,T;
int dis[maxm];
std::queue<int>q;
int mincost; bool rd[maxm];
bool spfa() {
for(int i=1;i<=tot;i++) dis[i] = 0x3f3f3f3f,rd[i]=0;
q.push(S); dis[S] = 0; rd[S] = 1;
while(q.size()) {
int x = q.front(); q.pop(); rd[x] = 0;
for(int it=la[x];it;it=nt[it]) {
int y = en[it];
if(v[it]&&dis[y]>dis[x]+len[it]) {
dis[y] = dis[x] + len[it];
if(!rd[y]) rd[y] = 1, q.push(y);
}
}
}
if(dis[T]==0x3f3f3f3f) return 0;
return 1;
}
int dfs(int x,int flow) {
if(x==T) {
rd[x] = 1; return flow;
}
int dlt = 0;
rd[x] = 1;
for(int it=cur[x];it;it=nt[it],cur[x]=it) {
int y = en[it];
if( (!rd[y] || y==T ) && v[it] && dis[x] + len[it] == dis[y] ) {
int tmp = dfs(y,std::min(v[it],flow-dlt));
v[it] -=tmp,v[it^1]+=tmp;
mincost+=len[it]*tmp,dlt+=tmp;
if(dlt==flow) break;
}
}
if(!dlt) dis[x] = -1;
return dlt;
}
int main() {
int maxflow = 0;
while(spfa()) {
for(int i=1;i<=tot;i++) cur[i] = la[i],rd[i]=0;
rd[T] = 1;
while(rd[T]) {
for(int i=1;i<=tot;i++) rd[i]=0;
maxflow += dfs(S,0x3f3f3f3f);
}
}