您的位置:首页 > 理论基础 > 计算机网络

网络流

2016-10-04 21:15 197 查看

网络流(最大流)

对于网络流求最大流的问题,可以想象为从起点倒水(倒无限多的水),然后问终点的水量.

所以一条路上为终点添加的流量为这条路上流量最小的路径.

(我把边能通过的流量称为流量,水的量也称为流量)

首先可以有一个贪心的想法,

每次可以让尽量大的流量通过边.



好像是已经是最大流了(已经没有水能到终点了),这种做法其实还有问题.

每次流入尽量多的水没有问题,每条边让尽量大的流量通过也没有问题,但是这样的贪心是没有远见的,它可能会把路堵住,导致答案变小(程序里的水不会自己流动).



(虽然我们可以很容易看出它的错误,但是数据一大就没有办法了,所以这样的贪心是不行的)

在普通的贪心失败后,往往可以采取回撤类型的贪心:

首先,按照前面的贪心一直加大流量.

然后,流量无法再加大了.于是,加入新的水流,让原来的水倒流回去,位新的水流腾出位置.

最后,无论如何也腾不出位置了,最大流就求出来了.

对于上个图,从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;
}
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流