zoukankan      html  css  js  c++  java
  • 网络流24题 -No.16 数字梯形问题

    问题描述
        给定一个由 n 行数字组成的数字梯形如下图所示。梯形的第一行有 m 个数字。从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
        规则 1:从梯形的顶至底的 m条路径互不相交。
        规则 2:从梯形的顶至底的 m条路径仅在数字结点处相交。
        规则 3:从梯形的顶至底的 m条路径允许在数字结点相交或边相交。
        2   3

        3   4   5

        9   10   9   1

        1   1   10   1   1

        1   1   10   12   1   1


    编程任务
        对于给定的数字梯形,分别按照规则 1,规则 2,和规则 3 计算出从梯形的顶至底的 m条路径,使这 m条路径经过的数字总和最大。

    数据输入

    输入文件的第 1 行中有 2个正整数 m和 n(m,n<=20),分别表示数字梯形的第一行有 m 个数字,共有 n 行。接下来的 n 行是数字梯形中各行的数字。
    第 1 行有 m个数字,第 2 行有 m+1 个数字,…。

    结果输出
    程序运行结束时,输出按照规则 1,规则 2,和规则 3 计算出的最大数字总和。

    输入文件示例

    2 5   

    2 3

    3 4 5

    9 10 9 1

    1 1 10 1 1

    1 1 10 12 1 1

    输出文件示例 

    66

    75

    77

    【问题分析】

    求图的最大权不相交路径及其变种,用费用最大流解决。

    【建模方法】

    规则(1)

    把梯形中每个位置抽象为两个点<i.a>,<i.b>,建立附加源S汇T。

    1、对于每个点i从<i.a>到<i.b>连接一条容量为1,费用为点i权值的有向边。
    2、从S向梯形顶层每个<i.a>连一条容量为1,费用为0的有向边。
    3、从梯形底层每个<i.b>向T连一条容量为1,费用为0的有向边。
    4、对于每个点i和下面的两个点j,分别连一条从<i.b>到<j.a>容量为1,费用为0的有向边。

    求最大费用最大流,费用流值就是结果。

    规则(2)

    把梯形中每个位置看做一个点i,建立附加源S汇T。

    1、从S向梯形顶层每个i连一条容量为1,费用为0的有向边。
    2、从梯形底层每个i向T连一条容量为无穷大,费用为点i权值的有向边。
    3、对于每个点i和下面的两个点j,分别连一条从i到j容量为1,费用为点i权值的有向边。

    求最大费用最大流,费用流值就是结果。

    规则(3)

    把梯形中每个位置看做一个点i,建立附加源S汇T。

    1、从S向梯形顶层每个i连一条容量为1,费用为0的有向边。
    2、从梯形底层每个i向T连一条容量为无穷大,费用为点i权值的有向边。
    3、对于每个点i和下面的两个点j,分别连一条从i到j容量为无穷大,费用为点i权值的有向边。

    求最大费用最大流,费用流值就是结果。

    【建模分析】

    对于规则1,要求路径完全不相交,也就是每个点最多只能被访问了一次,所以要把点拆分,之间连接容量为1的边。因为任意一条ST之间的路径都是一个解,在拆分的点内部的边费用设为点的权值,求最大费用最大流就是费用最大的m条路经。

    对于规则2,要求路径可以相交,但不能有重叠,此时可以不必拆点了。为了保证路径没有重叠,需要在相邻的两个点上限制流量为1,由于顶层的每个点只能用1次,S向顶层点流量限制也为1。费用只需设在相邻点的边上,求最大费用最大流即可。

    对于规则3,要求路径除了顶层每个点以外可以任意相交重叠。在规则2的基础上,取消除S到顶层顶点之间的边以外所有边的流量限制即可。
     

    代码:

      1 const
      2   maxn=1 << 30;
      3 
      4 var
      5   ot,cost,ne,cap,h:array[0..30000]of longint;
      6   g,pre,dis:array[0..1010]of longint;
      7   a,find:array[1..40,1..40]of longint;
      8   inq:array[0..1010]of boolean;
      9   e,s,t,c,i,n,m,ans,j:longint;
     10 
     11 procedure addedge(x,y,z,w:longint);
     12 begin
     13   ot[e]:=y; cap[e]:=z; ne[e]:=g[x]; cost[e]:=-w; g[x]:=e; inc(e);
     14   ot[e]:=x; cap[e]:=0; ne[e]:=g[y]; cost[e]:=w; g[y]:=e; inc(e);
     15 end;
     16 
     17 function min(a,b:longint):longint;
     18 begin
     19   if a<b then exit(a) else exit(b);
     20 end;
     21 
     22 function spfa:boolean;
     23 var
     24   x,y,l,r,p:longint;
     25 begin
     26   for i:=s to t do
     27     begin dis[i]:=maxn; inq[i]:=false; end;
     28   l:=0; r:=1; dis[s]:=0; inq[s]:=true; h[1]:=s; pre[s]:=-1;
     29   while l<r do
     30     begin
     31       inc(l);
     32       x:=h[l];
     33       p:=g[x];
     34       while p>-1 do
     35         begin
     36           y:=ot[p];
     37           if (cap[p]>0)and(dis[y]>dis[x]+cost[p])
     38             then begin
     39                    dis[y]:=dis[x]+cost[p]; pre[y]:=p;
     40                    if inq[y]=false
     41                      then begin inq[y]:=true; inc(r); h[r]:=y; end;
     42                  end;
     43           p:=ne[p];
     44         end;
     45       inq[x]:=false;
     46     end;
     47   exit(dis[t]<>maxn);
     48 end;
     49 
     50 function find_path:longint;
     51 var
     52   x,p,tmp,path:longint;
     53 begin
     54   x:=t; path:=maxn; tmp:=0;
     55   while x>s do
     56     begin
     57       p:=pre[x];
     58       path:=min(path,cap[p]);
     59       x:=ot[p xor 1];
     60     end;
     61   x:=t;
     62   while x>s do
     63     begin
     64       p:=pre[x];
     65       inc(tmp,path*cost[p]);
     66       inc(cap[p xor 1],path);
     67       dec(cap[p],path);
     68       x:=ot[p xor 1];
     69     end;
     70   exit(tmp);
     71 end;
     72 
     73 procedure work1;
     74 begin
     75   s:=0; t:=find[n,m+n-1]*2+1; ans:=0; e:=0;
     76   fillchar(g,sizeof(g),255);
     77   for i:=1 to n do
     78     for j:=1 to m+i-1 do addedge(find[i,j]*2-1,find[i,j]*2,1,a[i,j]);
     79   for i:=1 to m do addedge(s,2*i-1,1,0);
     80   for i:=find[n,1] to find[n,m+n-1] do addedge(2*i,t,1,0);
     81   for i:=1 to n-1 do
     82     for j:=1 to m+i-1 do
     83       begin
     84         addedge(find[i,j]*2,find[i+1,j]*2-1,1,0);
     85         addedge(find[i,j]*2,find[i+1,j+1]*2-1,1,0);
     86       end;
     87   while spfa do
     88     inc(ans,find_path);
     89   writeln(-ans);
     90 end;
     91 
     92 procedure work2(x:longint);
     93 begin
     94   s:=0; t:=find[n,m+n-1]+1; ans:=0; e:=0;
     95   fillchar(g,sizeof(g),255);
     96   for i:=1 to m do addedge(s,i,1,0);
     97   for i:=1 to m+n-1 do addedge(find[n,i],t,maxn,a[n,i]);
     98   for i:=1 to n-1 do
     99     for j:=1 to m+i-1 do
    100       if x=1
    101         then begin
    102                addedge(find[i,j],find[i+1,j],1,a[i,j]);
    103                addedge(find[i,j],find[i+1,j+1],1,a[i,j]);
    104              end
    105         else begin
    106                addedge(find[i,j],find[i+1,j],maxn,a[i,j]);
    107                addedge(find[i,j],find[i+1,j+1],maxn,a[i,j]);
    108              end;
    109   while spfa do
    110     inc(ans,find_path);
    111   writeln(-ans);
    112 end;
    113 
    114 begin
    115   readln(m,n);
    116   for i:=1 to n do
    117     for j:=1 to m+i-1 do
    118       begin
    119         read(a[i,j]);
    120         find[i,j]:=(2*m+i-2)*(i-1) div 2+j;
    121       end;
    122   work1;
    123   work2(1);
    124   work2(2);
    125 end.


  • 相关阅读:
    弹性计算双周刊 第24期
    【阿里云新品发布·周刊】第8期:数字化风暴已经来临!云+区块链,如何颠覆未来科技?
    洞见数据库前沿 阿里云数据库最强阵容 DTCC 2019 八大亮点抢先看
    开发者招聘节 | 2019阿里巴巴技术面试题分享(陆续放出)
    bzoj1856: [Scoi2010]字符串
    bzoj1257: [CQOI2007]余数之和sum
    bzoj1088: [SCOI2005]扫雷Mine
    noip2015 运输计划
    noip2015 子串
    noip2015 斗地主
  • 原文地址:https://www.cnblogs.com/kry-ssw-1314/p/4572238.html
Copyright © 2011-2022 走看看