网络流二十四题之二 —— 太空飞行计划问题(SHUT)


太空飞行计划问题


Description

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

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


Input

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


Output

1 行是实验编号;
2 <script type="math/tex" id="MathJax-Element-19">2</script> 行是仪器编号;
最后一行是净收益。


Sample Input

2 3
10 1 2
25 2 3
5 6 7


Sample Output

1 2
1 2 3
17


HINT

数据范围很小啦


Solution

因为是网络流里面的题,所以肯定要用网络流来做了啊。
弄个源点和一个汇点。
将源点连接每个实验,流量为实验的收益。
将每个仪器连接汇点,流量为装置的花费。
∵ 净收益=选择的实验的收益-选择的装置的花费。
∴ 净收益=(所有实验的收益-未选择的实验的收益)-选择的装置的花费。
即净收益=所有实验的收益-(未选择的实验的收益+选择的装置的花费(②))。
∵ 所有实验的收益是以给固定的值,所以我们只要使 ② 最小即可。

对应到网络中,我们发现 ② 正好是网络的一个割。
所以只要求最小割就行了。


Code

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <queue>  
  5.   
  6. #define Min(x,y) ((x)<(y)?(x):(y))  
  7.   
  8. using namespace std;  
  9.   
  10. const int INF=0x3f3f3f3f;  
  11.   
  12. int m,n,l,s,t,cnt;  
  13.   
  14. int low[1000],head[100],nxt[1000],data[1000];  
  15. int w[100];  
  16. int c[100];  
  17. int to[100][100];   
  18. int dis[100];  
  19.    
  20. char str[10000];  
  21. queue<int>q;  
  22.   
  23. void add(int x,int y,int z){  
  24.     nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;  
  25.     nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;   
  26. }  
  27.   
  28. bool BFS(){  
  29.     memset(dis,-1,sizeof dis);  
  30.     q.push(s);dis[s]=0;  
  31.     while(!q.empty()){  
  32.         int now=q.front();q.pop();  
  33.         for(int i=head[now];i!=-1;i=nxt[i])  
  34.             if(low[i]&&dis[data[i]]<0){dis[data[i]]=dis[now]+1;q.push(data[i]);}  
  35.     }  
  36.     return dis[t]>0;  
  37. }  
  38.   
  39. int dfs(int now,int flow){  
  40.     if(now==t)return flow;  
  41.     int Flow;  
  42.     for(int i=head[now];i!=-1;i=nxt[i]){  
  43.         if(low[i]&&dis[data[i]]==dis[now]+1){  
  44.             if(Flow=dfs(data[i],Min(flow,low[i]))){  
  45.                 low[i]-=Flow;  
  46.                 low[i^1]+=Flow;  
  47.                 return Flow;  
  48.             }  
  49.         }  
  50.     }  
  51.     return 0;  
  52. }  
  53.   
  54. int main(){  
  55.     int weight=0;  
  56.     scanf(”%d%d”,&m,&n);  
  57.     memset(head,-1,sizeof head);  
  58.     s=n+m+1,t=n+m+2;  
  59.     for(int i=1;i<=m;i++){  
  60.         scanf(”%d”,&w[i]);  
  61.         weight+=w[i];  
  62.         gets(str);  
  63.         l=strlen(str);  
  64.         int pos=0;  
  65.         while(pos<l){  
  66.             if(str[pos]<‘0’||str[pos]>‘9’){  
  67.                 while(str[pos]<‘0’||str[pos]>‘9’&&pos<l){pos++;}  
  68.                 if(pos==l)break;  
  69.                 to[i][0]++;  
  70.             }  
  71.             to[i][to[i][0]]=to[i][to[i][0]]*10+str[pos]-’0’;  
  72.             pos++;  
  73.         }  
  74.     }  
  75.     for(int i=1;i<=m;i++){  
  76.         add(s,i,w[i]);  
  77.         for(int j=1;j<=to[i][0];j++)add(i,to[i][j]+m,INF);  
  78.     }  
  79.     for(int i=1;i<=n;i++){  
  80.         scanf(”%d”,&c[i]);  
  81.         add(i+m,t,c[i]);  
  82.     }  
  83.     while(BFS()){  
  84.         int flag;  
  85.         while(flag=dfs(s,INF)){  
  86.             weight-=flag;  
  87.         }  
  88.     }  
  89.     BFS();  
  90.     for(int i=1;i<=m;i++)if(dis[i]>0)printf(“%d ”,i);  
  91.     printf(”\n”);   
  92.     for(int j=m+1;j<=m+n;j++)if(dis[j]>0)printf(“%d ”,j-m);  
  93.     printf(”\n%d\n”,weight);  
  94.     return 0;  
  95. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值