您的位置:首页 > 理论基础 > 计算机网络

【网络流24题】太空飞行计划问题  …

2015-02-02 19:24 381 查看
太空飞行计划问题(shut.cpp/c/pas)

【问题描述】

  W
教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj∈I。配置仪器Ik的费用为才Ck美元。实验Ej的赞助商已同意为该实验结果支付Pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

【编程任务】

  对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

Input

由文件shut.in提供输入数据。

  文件第1行有2 个正整数m和n。m是实验数,n是仪器数。接下来的m
行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。

Output

程序运行结束时,将最佳实验方案输出到文件shut.out 中。

  第1 行是实验编号;第2行是仪器编号;最后一行是净收益。

Sample Input

2 3

10 1 2

25 2 3

5 6 7

Sample Output

1 2

1 2 3

17

 

 

典型最小割问题

做之前看了不少网上的题解都是这样写的:

 

把每个实验看作二分图X集合中的顶点,每个设备看作二分图Y集合中的顶点,增加源S和汇T。

1、从S向每个Xi连接一条容量为该点收入的有向边。

2、从Yi向T连接一条容量为该点支出的有向边。

3、如果一个实验i需要设备j,连接一条从Xi到Yj容量为无穷大的有向边。

统计出所有实验的收入只和Total,求网络最大流Maxflow,最大收益就是Total-Maxflow。对应的解就是最小割划分出的S集合中的点,也就是最后一次
增广找到阻塞流时能从S访问到的顶点。
 

想了很久,才算差不多明白了:

割点分为两种,一种是割S到实验的边,一种是割器材到T的边。前者表示不做这个实验,后者表示买这种器材。至于为什么,可以这样想:

如图1(M代表无穷大),输入为

1 2

5 1 2

2 2





这个图的最大流是4,最小割也是4,割点就是两个仪器到T的边。答案就是买这两种器材。

 

 

如图2(M代表无穷大),输入为

1 2

5 1 2

3 3





这个图的最大流是5,最小割也是5,割点就是T到一个实验的边。答案就是不做这个实验。

 

基本上就是这么想,对于一个实验,如果完成这个实验的成本(器材费用)>实验的收益,那么最后找最小割点是,就会在S到这个实验的边上堵塞,就会割了这个边,就不做这个实验,不买这些器材。如果完成这个实验的成本(器材费用)<实验的收益,那么最后找最小割点是,就会在器材到T的边上堵塞,就会割了这个边,就买这些器材,就做这个实验。

所以就是最小割问题

但这个题解很坑跌。它教你找割点的方法有两个点过不了,因为会有这种特殊情况:有一个实验,做它的成本=它的价值。用题解的方法会不做,而数据则做。解决办法是,从T开始反着找,没到达的边就是割点。当然,刚才所说的特殊情况,在实验到T的边就会被割,就是说买这些器材。

const

 max=maxlongint;

type

 node=record

 s,b:longint;

 end;

 net=record

 c,f:longint;

 end;

var

 m,n,tot,a,s,t:longint;

 dian:array[1..100]of node;

 xian,xiann:array[1..100,1..100]of net;

procedure init;

var

 i,j,x:longint;

begin

 read(n,m);

 n:=n+1;

 for i:=2 to n do

  begin

  read(xian[1,i].c);

  while not eoln do

   begin

   
read(x);

   
xian[i,x+n].c:=max;

   end;

  end;

 for i:=1 to m do

  begin

  read(x);

  xian[n+i,m+n+1].c:=x;

  end;

 tot:=n+m+1;

end;

function find:longint;

var

 i:longint;

begin

 for i:=1 to tot do

  if
(dian[i].s<>0)and(dian[i].b=0) then
exit(i);

 exit(0);

end;

function ford:boolean;//标号法最大流

var

 i,j,k,x:longint;

begin

 fillchar(dian,sizeof(dian),0);

 dian[s].s:=s;

 while k<>t do

  begin

  k:=find;

  if k=0 then exit(false);

  for i:=1 to tot do

   if
(dian[i].s=0)and((xian[i,k].c>0)or(xian[k,i].c>0))
then

    begin

    if
(xian[k,i].c>xian[k,i].f) then dian[i].s:=k;

    if
(xian[i,k].c>0)and(xian[i,k].f>0)
then dian[i].s:=-k;

    end;

  dian[k].b:=1;

  end;

  j:=t;  a:=maxlongint;

  while j<>s
do

   begin

   i:=abs(dian[j].s);

   if dian[j].s>0
then x:=xian[i,j].c-xian[i,j].f;

   if dian[j].s<0
then x:=xian[j,i].f;

   if x<a then
a:=x;

   j:=i;

   end;

  exit(true);

end;

procedure change;

var

 i,j:longint;

begin

  j:=t;

  while j<>s
do

   begin

   i:=abs(dian[j].s);

   if dian[j].s>0
then inc(xian[i,j].f,a);

   if dian[j].s<0
then dec(xian[j,i].f,a);

   j:=i;

   end;

end;

procedure fan_ford;//反着找

var

 i,j,k:longint;

begin

 fillchar(dian,sizeof(dian),0);

 dian[t].s:=t;

 while k<>s do

  begin

  k:=find;

  if k=0 then exit;

  for i:=1 to tot do

   if
(dian[i].s=0)and((xian[i,k].c>0)or(xian[k,i].c>0))
then

    begin

    if
(xian[i,k].c>xian[i,k].f) then dian[i].s:=k;

    if
(xian[k,i].c>0)and(xian[k,i].f>0)
then dian[i].s:=-k;

    end;

  dian[k].b:=1;

  end;

end;

procedure print;

var

 i,j,cc,ff:longint;

begin

 cc:=0;

 ff:=0;

 fan_ford;

 for i:=2 to n do

  if dian[i].s=0 then write(i-1,' ');

  writeln;

 for i:=n+1 to tot-1 do

  if dian[i].s=0 then write(i-n,' ');

 writeln;

 for i:=1 to tot do

 begin

 cc:=cc+xian[1,i].c;

 ff:=ff+xian[1,i].f;

 end;

 writeln(cc-ff);

end;

procedure main;

begin

 s:=1;

 t:=tot;

 while ford do change;

 print;

end;

begin

 assign(input,'a.in');

 assign(output,'a.out');

 reset(input);

 rewrite(output);

 init;

 main;

 close(input);

 close(output);

end.

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: