最大网络流

来源:互联网 发布:毕加索蓝色时期 知乎 编辑:程序博客网 时间:2024/05/31 19:29

学完最大网络流,感觉是最大流的题套个模板就OK了

我的自己的理解是:网络流--网络&&流,我们把网络构建出来,明确什么东西当做流,然后就可以套模板了

看个例题

地震逃生

题目描述:汶川地震发生时,四川**中学正在上课,一看地震发生,老师们立刻带领x名学生逃跑,整个学校可以抽象地看成一个有向图,图中有n个点,m条边。1号点为教室,n号点为安全地带,每条边都只能容纳一定量的学生,超过楼就要倒塌,由于人数太多,校长决定让同学们分成几批逃生,只有第一批学生全部逃生完毕后,第二批学生才能从1号点出发逃生,现在请你帮校长算算,每批最多能运出多少个学生,x名学生分几批才能运完。

输入格式:第一行3个整数n,m,x(x<2^31,n<200,m<2000);以下m行,每行三个整数a,b,c(a<n,b>1,a<>b,0<c<200)描述一条边,分别代表从a点到b点有一条边,且可容纳c名学生。

输出格式:两个整数,分别表示每批最多能运出多少个学生,x名学生分几批才能运完。如果无法到达目的地(n号点)则输出“Oh no!!!”

样例输入:

6 7 7

1 2 1

1 4 2

2 3 1

4 5 1

4 3 1

3 6 2

5 6 1

样例输出:

3 3


分析:

典型的最大网络流的题目,就是以1为起始点,以n为目标点,一共有m条边的网络

一下逃跑的路线为构建的网络,学生的逃跑流量为改网络的流


虽然手写的不是很好看。。

从中我们可以看出路径

路径1=1--2--3--6

路径2=1--4--3--6

路径3=1--4--5--6


经过分析,不难看出,在1--6这几个点中,必须满足条件

1.  2--5的每个点的学生流进量和流出量相等

2.  1的流出量和6的流进量相等


因此,在每条路径中,我们的最大分流量应该是该路径的所有边能承受的流量中最小的那个(超过其中最小的那个的话,那条边就爆了)

于是我们对路径1进行计算,1--2是1,2--3是1,3--6是2,路径1的最大分流即为1

路径2 同理,最大分流为1,路径3的最大分流也是1

故总最大流为3条分流的和,即1+1+1=3

既然已经算出一次按这样路径的最大流是3,那么学生流动的次数就很好求了(直接整除喽)


贴上手写代码,注释写了半天..,求点赞

var capacity:array[1..200,1..200]of longint;//记下这张网络图    flow,pre:array[1..200]of longint;       //flow记录分流量,pre[i]表示某条路径中i这个节点是接在哪个点后面的    n,m,x,a1,b1,c1,maxstream,time:longint;    queue:array[1..100000]of longint;       //模拟栈的队列数组,c++可以不用这个,c++有专门的push和pop函数,更方便function min(a,b:longint):longint;          //求最小值的函数begin     if a>b then exit(b)     else exit(a);end;function bfs(src,des:longint):longint;      //搜索所有的路径,src是初始点,des是目标点var i,j,tail,index:longint;begin     fillchar(queue,sizeof(queue),0);     fillchar(pre,sizeof(pre),-1);          //填为-1表示这个点还没有它的父亲节点     pre[src]:=0;flow[src]:=$7fffff;        //初始化pre和flow,这里将$7fffff用作正无穷     tail:=1;queue[1]:=src;                 //将初始点入栈     while not(tail=0)do                    //判断栈中是否有元素,没有的话说明再也找不到路径了     begin          index:=queue[tail];               //从栈中取出栈顶端的,也就是队列的最后一个点          dec(tail);                        //出栈          if(index=des)then break;          //如果这个点是目标点,说明这条路找到了,就终止循环          for i:=1 to n do                  //遍历每个节点          begin               if(i<>src)and(capacity[index,i]>0)and(pre[i]=-1)then //判断当前走到的点index是否和i点相连,并且还未走过(即pre[i]=-1)               begin                    pre[i]:=index;                                  //记录i节点是由index点过来的                    flow[i]:=min(capacity[index,i],flow[index]);    //取(index点的最大流)和(从index到i的最大容量)二者最小值                    inc(tail);                    queue[tail]:=i;                                 //将i点入栈               end;          end;     end;     if(pre[des]=-1)then exit(-1)           //如果目标点的父亲节点是-1,即找不到与目标点相连的路径     else exit(flow[des]);                  //如果能找到路径,输出最大的分流end;function floyd(src,des:longint):longint;    //将所有分流遍历完统计var increasement,sumflow,k:longint;begin     sumflow:=0;     increasement:=bfs(src,des);     while(increasement<>-1)do              //如果第一遍的搜索不能找到分流,那么这段代码就不执行,答案就是学生无法撤离     begin                                  //如果有分流,就不断的搜索,不断的合并分流          k:=des;                           //while里面的语句因为是套模板,这里没什么用..          while(k<>src)do          begin               dec(capacity[pre[k],k],increasement);               inc(capacity[k,pre[k]],increasement);               k:=pre[k];          end;          inc(sumflow,increasement);        //合并分流          increasement:=bfs(src,des);     end;     exit(sumflow);                         //得到最大网络流end;procedure init;var i,j:longint;begin     readln(n,m,x);     for i:=1 to m do     begin          readln(a1,b1,c1);          if a1=b1 then continue;           //判断只有目标点和起始点的特殊情况          inc(capacity[a1,b1],c1);          //考虑到2个点之间多条边的情况,所以不直接赋值     end;end;begin     init;                                  //初始化     maxstream:=floyd(1,n);                 //遍历得到最大流     if maxstream=0 then writeln('Oh no!!!')//最大流为0即没有路径能通过,换句话说,它的最大流量就是0     else     begin          time:=x div maxstream;            //对学生的批数进行处理输出          if x mod maxstream<>0 then inc(time);          writeln(maxstream,' ',time);     end;     readln;readln;end.



0 0