网络流(最大流)
对于网络流求最大流的问题,可以想象为从起点倒水(倒无限多的水),然后问终点的水量.
所以一条路上为终点添加的流量为这条路上流量最小的路径.
(我把边能通过的流量称为流量,水的量也称为流量)
首先可以有一个贪心的想法,
每次可以让尽量大的流量通过边.
好像是已经是最大流了(已经没有水能到终点了),这种做法其实还有问题.
每次流入尽量多的水没有问题,每条边让尽量大的流量通过也没有问题,但是这样的贪心是没有远见的,它可能会把路堵住,导致答案变小(程序里的水不会自己流动).
(虽然我们可以很容易看出它的错误,但是数据一大就没有办法了,所以这样的贪心是不行的)
在普通的贪心失败后,往往可以采取回撤类型的贪心:
首先,按照前面的贪心一直加大流量.
然后,流量无法再加大了.于是,加入新的水流,让原来的水倒流回去,位新的水流腾出位置.
最后,无论如何也腾不出位置了,最大流就求出来了.
对于上个图,从3流入30的流量,但是这部分的流量没有地方去,所以让4倒流10的流量,经过5和2流向终点,并从3中得到10的流量.于是,我们增加了10的流量
这样好像很复杂(还要从源头修改),
但是这部分的操作也可以直接简化为3-5-2流动的过程
所以,在储存每一条边的时候,再额外存储一条反向边(它的可以通过的流量为0).
当水流通过时,正向边的流量减小,反向边的流向增大(表示正向可以通过的流量减小,而可以倒流的流量增大)
最后,当无法增大流量时,就求出了最大流.
const int M=1005;
const int INF=1<<28;
struct node {
int to,cap,bk;
/*e[to][bk]指向自己*/
};
struct graph {
vector <node> e[M];
bool mark[M];
void clear() {
for (int i=0;i<M;++i) e[i].clear();//清空;
}
void addedge(int st,int ed,int cap) {
e[st].push_back((node){ed,cap,(int)e[ed].size()});
e[ed].push_back((node){st,0,(int)e[st].size()-1});//储存反边 (流量为0)
}
int dfs(int st,int ed,int f) {
if (st==ed) return f;
mark[st]=true;
for (int i=0;i<(int)e[st].size();++i) {
node &now=e[st][i];
if (mark[now.to]||!now.cap) continue;
int d=dfs(now.to,ed,min(f,now.cap));
if (d) {
now.cap-=d;
e[now.to][now.bk].cap+=d;//为反边增加大流量
return d;
}
}
return 0;
}
int flow(int st,int ed) {
int flow=0;
while (true) {
memset(mark,0,sizeof(mark));
int f=dfs(st,ed,INF);
if (!f) return flow;
flow+=f;
}
}
};