网络流24题之二 太空飞行计划 最大权闭合子图

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

编程任务:
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m和 n。m 是实验数,n 是仪
器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费
用;接着是该实验需要用到的若干仪器的编号。最后一行的 n 个数是配置每个仪器的费用。

结果输出:
程序运行结束时,将最佳实验方案输出到文件 output.txt 中。第 1 行是实验编号;第 2
行是仪器编号;最后一行是净收益。
输入文件示例  输出文件示例

input.txt  output.txt
2 3
10 1 2
25 2 3
5 6 7
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访问到的顶点的集合。


写完这题后我学到了如何解决最大权闭合子图的问题,还有一种新的求最小割的方法:从源点开始用一次fill,所有源点能到达的点即为最小割的S集合。


代码:

var
  n,m,x,tot,ans,i,s,t:longint;
  v:array[0..100] of boolean;
  d:array[0..100] of longint;
  c:array[0..100,0..100] of longint;

function bfs:boolean;
var
  head,tail,i,u:longint;
  state:array[1..100] of longint;
begin
  head:=0;
  tail:=1;
  state[1]:=s;
  fillchar(d,sizeof(d),0);
  d[s]:=1;
  repeat
    inc(head);
    u:=state[head];
    for i:=s to t do
      if (c[u,i]>0)and(d[i]=0) then
      begin
        d[i]:=d[u]+1;
        inc(tail);
        state[tail]:=i;
        if i=t then exit(true);
      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
  i,f,ret:longint;
begin
  if x=t then exit(maxf);
  ret:=0;
  for i:=s to t do
    if (c[x,i]>0)and(d[i]=d[x]+1) then
    begin
      f:=dfs(i,min(maxf-ret,c[x,i]));
      dec(c[x,i],f);
      inc(c[i,x],f);
      ret:=ret+f;
      if maxf=ret then exit(ret);
    end;
  dfs:=ret;
end;

procedure fill(x:longint);
var
  i:longint;
begin
  v[x]:=false;
  for i:=s to t do
    if (c[x,i]>0)and(v[i]) then
      fill(i);
end;

begin
 // assign(input,'shut.in');
 // assign(output,'shut.out');
  reset(input);
  rewrite(output);
  readln(m,n);
  s:=0; t:=n+m+1;
  for i:=1 to m do
  begin
    read(x);
    c[s,i]:=x;
    tot:=tot+x;
    while not eoln do
    begin
      read(x);
      c[i,x+m]:=maxlongint div 3;
    end;
    readln;
  end;
  for i:=1 to n do
  begin
    read(x);
    c[m+i,t]:=x;
  end;
  while bfs do ans:=ans+dfs(s,maxlongint div 3);
  fillchar(v,sizeof(v),true);
  fill(s);
  for i:=1 to m do
    if not v[i] then write(i,' ');
  writeln;
  for i:=m+1 to n+m do
    if not v[i] then write(i-m,' ');
  writeln;
  writeln(tot-ans);
  close(input);
  close(output);
end.


你可能感兴趣的:(网络流24题之二 太空飞行计划 最大权闭合子图)