太空飞行计划问题

题目描述

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

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

输入输出格式

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

输出格式:
第1 行是实验编号;第2行是仪器编号;最后一行是净收益。

输入输出样例

输入样例#1:
2 3
10 1 2
25 2 3
5 6 7
输出样例#1:
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
  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
  begin

   ans:=ans+dfs(s,maxlongint div 3);
  end;
  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);

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值