【网络流24题-2】太空飞行计划
Description
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1, I2,…In}。 实验 Ej需要用到的仪器是I的子集。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
【编程任务】:
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
【编程任务】:
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
Input
输入文件的第1行有 2 个正整数 m和n(0 < m,n <= 100)。m是实验数,n是仪器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号,以一个0作为行的结束标记。最后一行的 n个数是配置每个仪器的费用。
Output
输出文件的第1行是实验编号;第2行是仪器编号;最后一行是净收益。
Sample Input
2 3 10 1 2 0 25 2 3 0 5 6 7
Sample Output
1 2 1 2 3 17
Solution
给出各项收入所需要的支出,求最大利润。这种形式的题目可以转化为最大权闭合图问题,那么我们所求的最大利润即为
最大利润=总收入-最大权闭合图权值 即
最大利润=总收入-最大流
构建一个二分图。将利润,即为项目赞助从源点连上一条容量为赞助费用的有向边,再把每一个实验器材向汇点连一条容量为器材费用的有向边。输入原图中项目对应的器材时,连上一条容量为正无穷的边。
将总赞助相加,就是不记损耗下的总收入。从源点开始跑一遍最大流,就得出了应该减去的损耗,用总收入减去此时的最大流即可得到最大利润。
最后从源点dfs一遍后,枚举每一条边。若某点被访问,则一定存在于最优解中,按照顺序输出即可。
CODE
注意二分图的思想
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
struct Pipe{int next,to,flow;}pipe[50005];
int h[505],cnt=1;
inline void add(int x,int y,int z){
pipe[++cnt].to=y;pipe[cnt].next=h[x];h[x]=cnt;pipe[cnt].flow=z;
pipe[++cnt].to=x;pipe[cnt].next=h[y];h[y]=cnt;pipe[cnt].flow=0;
return ;
}
inline int read(){
char c;int rec=0,f=1;
while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec*f;
}
int N,M,n;
int ans=0,tot=0;
int Gap[505],dis[505],vis[505];
int st,ed;
inline int SAP(int v,int maxflow){
if(v==ed)return maxflow;
int temp=maxflow,i,j,p;
for(i=h[v];i;i=pipe[i].next){
j=pipe[i].to;
if(pipe[i].flow&&dis[v]==dis[j]+1){
p=SAP(j,min(pipe[i].flow,temp));
temp-=p;pipe[i].flow-=p;pipe[i^1].flow+=p;
if(temp==0||dis[st]==n)return maxflow-temp;
}
}
if(--Gap[dis[v]]==0)dis[st]=n;
else Gap[++dis[v]]++;
return maxflow-temp;
}
inline void dfs(int v){
vis[v]=1;//标记访问
int i,j;
for(i=h[v];i;i=pipe[i].next){
j=pipe[i].to;
if(vis[j]==0&&pipe[i].flow)dfs(j);
}
return ;
}
int main(){
N=read();M=read();
st=0;ed=N+M+1;n=ed+1;
int i,j,x;
for(i=1;i<=N;i++){
x=read();tot+=x;
add(0,i,x);//源点到项目
while(x=read()){
if(x==0)break;
add(i,x+N,inf);//项目到器材
}
}
for(i=1;i<=M;i++){
x=read();
add(i+N,ed,x);//器材到汇点
}
Gap[0]=n;
while(dis[st]<n)ans+=SAP(st,inf);
dfs(st);//搜索残余网络
for(i=1;i<=N;i++)if(vis[i])cout<<i<<" ";cout<<endl;
for(i=N+1;i<=N+M;i++)if(vis[i])cout<<i-N<<" ";cout<<endl;
cout<<tot-ans;
return 0;
}