算法设计 最大网络流
最大网络流问题是图论中的经典问题,以前只是学习了Ford-Fulkerson方法,趁现在
比较“轻闲”把网络流的Push-relabel方法也学习一下。我们知道利用最短路径的Ford-Fulkerson方法实现算法,其
时间复杂度可以达到O(VE^2),而Push-relabel方法的算法实现(relabel-to-front)的复杂度可以达到O(V^3).
先说明一下基本概念:
c(u,v)代表网络管道(u,v)之间的流量限制,其值总是大于零的(c(u,v)=0 <=>no edge exist between u and v)
f(u,v)代表网络管道(u,v)之间的实际流量,当然不能超过c(u,v)的限制,且f(u,v)=-f(v,u)。
residual网络代表所有的剩余流组成的网络且该网络上的限制cf(u,v)=c(u,v)-f(u,v)(c,f分别是原网络的参数)
该网络可以表示为G = (V,Ef)。
Ford-Fulkerson方法的思想是每次都找到一个增广路径,然后填满该增广路径上的所有瓶颈流量。不断找到增广路径直到
没有为止,这样就可以找到一种最大流方案。该方法是从全局考虑每个路径的能力,让每一个路径都尽力而为,而Push-relabel
方法则将流量调整的权利下放到每一个节点,通过节点讲多余的流量挤出网络从而是网络的流量达到最大。那么怎么样某
一个节点能够讲他多余出来的流挤到下一个节点呢,就是该节点的势比较高,就是高度比较高,这里面有一个
Admissible edge((u,v)属于Ef并且h(u)=h(v)+1(h就是高度))的概念,就是能够通过这个edge将多余流量挤出去的管道。
通过反复的挤出流最终达到流量的最大。具体参考(算法导论),导论中先讲了Push Relabel 方法,然后介绍了一个基于该方法
的relabel-to-front算法。期中discharge方法相当于将自己多余的流挤给其他人,而总的relabel-to-front方法就是不停的调整
直到没有人可以将自己多余的流挤个其他人为止,这里在push的时候会因为管道能力的问题将多余流量扔掉,最后到达T的时候的
流量就是最后的最大流。
下面是relabel-to-front算法的具体实现
注意要使用g++编译器,vc需要将变量声明修改一下
最大网络流问题是图论中的经典问题,以前只是学习了Ford-Fulkerson方法,趁现在
比较“轻闲”把网络流的Push-relabel方法也学习一下。我们知道利用最短路径的Ford-Fulkerson方法实现算法,其
时间复杂度可以达到O(VE^2),而Push-relabel方法的算法实现(relabel-to-front)的复杂度可以达到O(V^3).
先说明一下基本概念:
c(u,v)代表网络管道(u,v)之间的流量限制,其值总是大于零的(c(u,v)=0 <=>no edge exist between u and v)
f(u,v)代表网络管道(u,v)之间的实际流量,当然不能超过c(u,v)的限制,且f(u,v)=-f(v,u)。
residual网络代表所有的剩余流组成的网络且该网络上的限制cf(u,v)=c(u,v)-f(u,v)(c,f分别是原网络的参数)
该网络可以表示为G = (V,Ef)。
Ford-Fulkerson方法的思想是每次都找到一个增广路径,然后填满该增广路径上的所有瓶颈流量。不断找到增广路径直到
没有为止,这样就可以找到一种最大流方案。该方法是从全局考虑每个路径的能力,让每一个路径都尽力而为,而Push-relabel
方法则将流量调整的权利下放到每一个节点,通过节点讲多余的流量挤出网络从而是网络的流量达到最大。那么怎么样某
一个节点能够讲他多余出来的流挤到下一个节点呢,就是该节点的势比较高,就是高度比较高,这里面有一个
Admissible edge((u,v)属于Ef并且h(u)=h(v)+1(h就是高度))的概念,就是能够通过这个edge将多余流量挤出去的管道。
通过反复的挤出流最终达到流量的最大。具体参考(算法导论),导论中先讲了Push Relabel 方法,然后介绍了一个基于该方法
的relabel-to-front算法。期中discharge方法相当于将自己多余的流挤给其他人,而总的relabel-to-front方法就是不停的调整
直到没有人可以将自己多余的流挤个其他人为止,这里在push的时候会因为管道能力的问题将多余流量扔掉,最后到达T的时候的
流量就是最后的最大流。
下面是relabel-to-front算法的具体实现
注意要使用g++编译器,vc需要将变量声明修改一下
- #include <iostream>
- #include <climits>
- using namespace std;
- #define BUF 100
- int f[BUF][BUF];
- int c[BUF][BUF];
- struct Edge{
- int v;
- Edge *next;
- };
- struct Node{
- int h;
- int ef;
- Edge *firste;
- };
- Node graph[BUF];
- Edge *current[BUF];
- struct List{
- int n;
- List *next;
- };
- List *L;
- int N;
- int S,T;
- /*
- sample input:
- 6 1 6 10
- 1 2 16
- 1 4 13
- 2 4 10
- 4 2 4
- 2 3 12
- 3 4 9
- 4 5 14
- 5 3 7
- 3 6 20
- 5 6 4
- the first line represent |V|=6,S=1,T=6,|E|=10.
- then the next ten lines represent 10 edges of the graph.
- */
- void init_graph()
- {
- int eN;
- scanf("%d%d%d%d",&N,&S,&T,&eN);
- for(int i=1;i<N;i++)
- graph[i].firste = NULL;
- for(int i=1;i<=eN;i++){
- int s,e;
- scanf("%d%d",&s,&e);
- if(c[e][s]==0){
- Edge *e1 = new Edge();
- Edge *e2 = new Edge();
- e1->v = s,e2->v = e;
- e1->next = graph[e].firste;
- e2->next = graph[s].firste;
- graph[e].firste = e1;
- graph[s].firste = e2;
- }
- scanf("%d",&c[s][e]);
- }
- }
- //to print the debug information
- void print()
- {
- for(int i=1;i<=N;i++){
- for(int j=1;j<=N;j++)
- printf("%d ",c[i][j]);
- printf("/n");
- }
- printf("/n");
- for(int i=1;i<=N;i++){
- for(int j=1;j<=N;j++)
- printf("%d ",f[i][j]);
- printf("/n");
- }
- printf("/n");
- List *p = L->next;
- while(p!=NULL){
- printf("%d ",p->n);
- p = p->next;
- }
- printf("/n");
- for(int i=1;i<=N;i++)
- printf("%d ",graph[i].ef);
- printf("/n/n");
- }
- void init_preflow()
- {
- for(int i=0;i<N;i++){
- graph[i].h = 0;
- graph[i].ef = 0;
- }
- fill_n(&f[0][0],BUF*BUF,0);
- graph[S].h = N;
- for(Edge *p=graph[S].firste;p!=NULL;p=p->next){
- f[S][p->v] = c[S][p->v];
- f[p->v][S] = -1*c[S][p->v];
- graph[p->v].ef = c[S][p->v];
- graph[S].ef -= c[S][p->v];
- }
- }
- void init_list()
- {
- L = new List();
- List *p = L;
- for(int i=1;i<=N;i++)
- if(i!=S&&i!=T){
- p->next = new List();
- p->next->n = i;
- p = p->next;
- }
- p->next = NULL;
- }
- // the next four functions are the core of the relabel to front algorithm.
- void push(int u,int v)
- {
- if(graph[u].ef>0&&graph[u].h==(graph[v].h+1)){
- int df = c[u][v]-f[u][v];
- df = min(df,graph[u].ef);
- f[u][v] += df;
- f[v][u] = -1*f[u][v];
- graph[u].ef -= df;
- graph[v].ef += df;
- }
- }
- void relabel(int u)
- {
- if(graph[u].ef>0){
- int low = INT_MAX;
- bool have = false;
- for(Edge *p=graph[u].firste;p!=NULL;p=p->next)
- if(c[u][p->v]-f[u][p->v]>0&&graph[u].h<=graph[p->v].h){
- low = min(low,graph[p->v].h);
- have = true;
- }
- if(have)
- graph[u].h = low+1;
- }
- }
- void discharge(int u)
- {
- while(graph[u].ef>0){
- Edge *pv = current[u];
- if(pv==NULL){
- relabel(u);
- current[u] = graph[u].firste;
- }else if(c[u][pv->v]-f[u][pv->v]>0&&graph[u].h==(graph[pv->v].h+1)){
- push(u,pv->v);
- }else {
- current[u] = pv->next;
- }
- }
- }
- void relabel_front()
- {
- init_preflow();
- init_list();
- for(int i=1;i<=N;i++)
- if(i!=S&&i!=T)
- current[i] = graph[i].firste;
- List *q = L;
- List *p = L->next;
- while(p!=NULL){
- int oh = graph[p->n].h;
- discharge(p->n);
- if(graph[p->n].h>oh){
- q->next = p->next;
- q = L;
- p->next = q->next;
- q->next = p;
- }
- p = p->next;
- q = q->next;
- // print();
- }
- }
- int main()
- {
- freopen("rf1.in","r",stdin);
- freopen("rf.out","w",stdout);
- init_graph();
- relabel_front();
- printf("%d/n",-1*graph[S].ef);
- return 0;
- }