网络流24题之十三 星际转移 分层图网络流

来源:互联网 发布:淘宝数据分析网站 编辑:程序博客网 时间:2024/05/17 21:32
算法实现题 8-13  星际转移问题(习题 8-26 )
?问题描述:
由于人类对自然资源的消耗,人们意识到大约在 2300 年之后,地球就不能再居住了。
于是在月球上建立了新的绿地,以便在需要时移民。令人意想不到的是,2177 年冬由于未
知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。现有 n 个太空站
位于地球与月球之间,且有 m 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限
多的人,而每艘太空船 i 只可容纳 H[i]个人。每艘太空船将周期性地停靠一系列的太空站,
例如:(1,3,4)表示该太空船将周期性地停靠太空站 134134134…。每一艘太空船从一个太
空站驶往任一太空站耗时均为 1。人们只能在太空船停靠太空站(或月球、地球)时上、下船。
初始时所有人全在地球上,太空船全在初始站。试设计一个算法,找出让所有人尽快地全部
转移到月球上的运输方案。
编程任务:
对于给定的太空船的信息,找到让所有人尽快地全部转移到月球上的运输方案。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 3 个正整数 n(太空站个数) ,m(太空船
个数)和 k(需要运送的地球上的人的个数) 。其中 1<=m<=13, 1<=n<=20, 1<=k<=50。
接下来的 m 行给出太空船的信息。第 i+1 行说明太空船 pi。第 1 个数表示 pi 可容纳的
人数 Hpi;第 2 个数表示 pi 一个周期停靠的太空站个数 r,1<=r<=n+2;随后 r 个数是停靠
的太空站的编号(Si1,Si2,…,Sir),地球用 0 表示,月球用-1 表示。时刻 0 时,所有太空船都
在初始站,然后开始运行。在时刻 1,2,3…等正点时刻各艘太空船停靠相应的太空站。人
只有在 0,1,2…等正点时刻才能上下太空船。
? 结果输出:
程序运行结束时,将全部人员安全转移所需的时间输出到文件 output.txt 中。如果问题
无解,则输出 0。
输入文件示例  输出文件示例
input.txt 
2 2 1
1 3 0 1 2

1 3 1 2 –1

output.txt

5

【问题分析】
分层图网络流问题,枚举答案,构造网络流判定。
【建模方法】
首先判断从地球到月球是否存在一条路线,如果不存在那么无解,否则把每个太空站按照每天拆分成d个点,<i,d>表示第i个站第d天。建立附加源S汇T,顺序枚举答案Day。
1、对于第Day天,从S到<0,Day>连接一条容量为无穷大的有向边。
2、从<-1,Day>到T连接一条容量为无穷大的有向边。
3、对于第i个太空船,设第Day-1天在a处,第Day天在b处,从<a,Day-1>到<b,Day>连接一条容量为该太空船容量的有向边。
4、对于第i个太空站,从<i,Day-1>到<i,Day>连接一条容量为无穷大的有向边。
5、求当前网络最大流,如果最大流量大于等于地球上人数K,停止枚举,当前Day值就是答案。
【建模分析】
我们把网络优化问题转化为枚举答案+可行性判定问题。枚举天数,按天数把图分层,因为乘船每坐一站天数都要增加一,把太空船航线抽象成图中的一条边,跨图的两层。由于太空船容量有限,边上也要加上容量限制。除了坐船以外,人还可以在某个空间站等待下一班太空船的到来,所以每个点要与下一层同一点连接一条容量为无穷的边。这样在层限制的图上求出的网络最大流就是在当前天数以内能够从地面到月球的最多的人数,该人数随天数递增不递减,存在单调性。所以可以枚举答案或二分答案,用网络流判定。网络流判定问题更适合枚举答案,而不是二分,因为新增一些点和边只需要在原有的基础上增广,不必重新求网络流。


代码:

const  maxn=1000;var  e,tot,day,s,t,i,j,x,y,n,m,k:longint;  d,last:array[-3..maxn] of longint;  state:array[1..maxn] of longint;  h:array[1..20] of longint;  a:array[1..20,0..20] of longint;  side:array[1..100000] of record    x,y,c,op,next:longint;  end;function point(x,day:longint):longint;begin  point:=day*(n+2)+x;end;procedure add(x,y,c:longint);begin  inc(e);  side[e].x:=x; side[e].y:=y; side[e].c:=c; side[e].op:=e+1;  side[e].next:=last[x]; last[x]:=e;  inc(e);  side[e].x:=y; side[e].y:=x; side[e].c:=0; side[e].op:=e-1;  side[e].next:=last[y]; last[y]:=e;end;function bfs:boolean;var  head,tail,u,i:longint;begin  fillchar(d,sizeof(d),0);  head:=0;  tail:=1;  state[1]:=s;  d[s]:=1;  repeat    inc(head);    u:=state[head];    i:=last[u];    while i>0 do      with side[i] do      begin        if (c>0)and(d[y]=0) then        begin         d[y]:=d[x]+1;         inc(tail);         state[tail]:=y;         if y=t then exit(true);        end;        i:=next;      end;  until head>=tail;  bfs:=false;end;function min(x,y:longint):longint;begin  if x<y then exit(x)         else exit(y);end;function dfs(x,maxf:longint):longint;var  ret,f,i:longint;begin  if x=t then exit(maxf);  ret:=0;  i:=last[x];  while i>0 do    with side[i] do    begin      if (c>0)and(d[y]=d[x]+1) then      begin        f:=dfs(y,min(c,maxf-ret));        ret:=ret+f;        dec(c,f);        inc(side[op].c,f);        if ret=maxf then break;      end;      i:=next;    end;  dfs:=ret;end;begin  assign(input,'home.in');  assign(output,'home.out');  reset(input);  rewrite(output);  readln(n,m,k);  for i:=1 to m do  begin    read(h[i]);    read(a[i,0]);    for j:=1 to a[i,0] do      read(a[i,j]);    readln;  end;  day:=0;  s:=-2;  t:=-3;  add(s,point(0,0),maxlongint);  add(point(-1,0),t,maxlongint);  while tot<k do  begin    if day>100 then break;    inc(day);    add(s,point(0,day),maxlongint);    add(point(-1,day),t,maxlongint);    for i:=1 to m do    begin      x:=day mod a[i,0]+1;      y:=x-1;      if y=0 then y:=a[i,0];      add(point(a[i,y],day-1),point(a[i,x],day),h[i]);    end;    for i:=-1 to n do      add(point(i,day-1),point(i,day),maxlongint);    while bfs do tot:=tot+dfs(s,maxlongint);  end;  if tot>0    then writeln(day)    else writeln(0);  close(input);  close(output);end.


0 0