洛谷 P2762 太空飞行计划问题
题目背景
题目描述
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
说明
感谢@zhouyonglong 提供spj//spj好评
解题思路
最大权闭合子图的入门题,这个模型我是看这篇博文看懂的。图要这样建——
从S向所有实验连边,边容量为该实验收入,从每个实验向每个需要的设备连边,边权为inf,从每个设备向T连边,边权为该设备费用。
答案为所有实验的总收入(不用减去成本)的总和减去上图从S到T的最大流。
源代码
#include<queue> #include<cstdio> #include<cstring> int n,m; int s,t; struct Edge{ int next,to,f; }e[100010]={0}; int cnt=2,head[100010]={0}; void add(int u,int v,int f) { e[cnt]={head[u],v,f}; head[u]=cnt++; e[cnt]={head[v],u,0}; head[v]=cnt++; } int dis[100010]={0}; bool vis[100010]={0}; bool bfs() { memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s]=1; std::queue<int> q; q.push(s); vis[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(!dis[v]&&e[i].f>0) { dis[v]=dis[u]+1; q.push(v); vis[v]=1; } } } return dis[t]!=0; } int dfs(int u,int flow) { if(u==t||flow==0) return flow; int flow_sum=0; for(int i=head[u];i;i=e[i].next) { int v=e[i].to,f=e[i].f; if(dis[v]!=dis[u]+1||f==0) continue; int temp=dfs(v,std::min(flow-flow_sum,f)); e[i].f-=temp; e[i^1].f+=temp; flow_sum+=temp; if(flow_sum>=flow) break; } if(!flow_sum) dis[u]=-1; return flow_sum; } int dinic() { int ans=0; while(bfs()) { while(int temp=dfs(s,0x7fffffff)) ans+=temp; } return ans; } int main() { //freopen("shuttle.in","r",stdin); //freopen("shuttle.out","w",stdout); scanf("%d%d",&m,&n); s=n+m+1,t=s+1; int ans=0; for(int i=1,w;i<=m;i++) { scanf("%d",&w); ans+=w; add(s,i,w); char ch=getchar(); while(ch==' ') { scanf("%d%c",&w,&ch); add(i,w+m,0x7fffffff); } } for(int i=1,w;i<=n;i++) { scanf("%d",&w); add(m+i,t,w); } int temp=ans-dinic(); for(int i=1;i<=m;i++) if(vis[i]) printf("%d ",i); printf("\n"); for(int i=1;i<=n;i++) if(vis[i+m]) printf("%d ",i); /*for(int i=head[s];i;i=e[i].next) if(e[i^1].f>0) printf("%d ",e[i].to); printf("\n"); for(int i=head[t];i;i=e[i].next) if(e[i].f>0) printf("%d ",e[i].to-m); */ putchar('\n'); printf("%d\n",temp); return 0; }