网络流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.


原文地址:https://www.cnblogs.com/kry-ssw-1314/p/4572238.html