太空飞行计划问题-最大权闭合图
题目描述
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行是仪器编号;最后一行是净收益。
输入输出样例
说明
感谢@FlierKing 提供spj
是最大权闭合图的裸题,是看了 胡伯涛《最小割模型在信息学竞赛中的应用》论文来的,上面对最大权闭合图的做法进行了证明,虽然我并没看太懂= =
做法就是建立S向实验点连边,cap为实验奖励,仪器点向T连边,cap为仪器费用,实验-仪器连边cap为inf然后跑最小割,答案就是所有实验的奖励总和减去这个最小割就是最大的净赚钱数。
最后输出方案的做法: 将dinic过的图从S开始dfs,能访问到的实验点就是方案中包含的实验。
我是这么理解的,净赚费用 = 实验总奖励 -(没有做的实验 + 仪器费用) ,使得括号内的费用最小答案就会最大了,对建好的图跑最小割,如果实验点最后仍与S连接说明没有割去这个实验,但是割去了这个实验所需的所有仪器。含义就是如果做某个实验是可以赚钱的就会割去他所需的仪器,如果是赔本的就会割去这个实验,正好对应了括号内的费用!
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x3f3f3f3f 4 struct Edge{ 5 int v,cap,flow,next; 6 }e[100010]; 7 int tot,first[110],S,T,N,M; 8 int d[110],cur[110]; 9 bool vis[110],vis2[110]; 10 char tools[10000]; 11 void add(int u,int v,int cap){ 12 e[tot]=Edge{v,cap,0,first[u]}; 13 first[u]=tot++; 14 e[tot]=Edge{u,0,0,first[v]}; 15 first[v]=tot++; 16 } 17 int bfs(){ 18 memset(vis,0,sizeof(vis)); 19 queue<int>q; 20 q.push(S); 21 vis[S]=1; 22 d[S]=0; 23 while(!q.empty()){ 24 int u=q.front(); 25 q.pop(); 26 for(int i=first[u];~i;i=e[i].next){ 27 if(!vis[e[i].v] && e[i].cap>e[i].flow){ 28 d[e[i].v]=d[u]+1; 29 vis[e[i].v]=1; 30 q.push(e[i].v); 31 } 32 } 33 } 34 return vis[T]; 35 } 36 int dfs(int u,int a){ 37 if(u==T || a==0)return a; 38 int flow=0,f; 39 for(int &i=cur[u];~i;i=e[i].next){ 40 if(d[e[i].v]==d[u]+1 && (f=dfs(e[i].v,min(a,e[i].cap-e[i].flow)))>0){ 41 e[i].flow+=f; 42 e[i^1].flow-=f; 43 flow+=f; 44 a-=f; 45 if(a==0)break; 46 } 47 } 48 return flow; 49 } 50 int dinic(){ 51 int ans=0; 52 while(bfs()){ 53 for(int i=0;i<=T;++i)cur[i]=first[i]; 54 ans+=dfs(S,inf); 55 } 56 return ans; 57 } 58 void gao(int u){ 59 vis[u]=1; 60 for(int i=first[u];~i;i=e[i].next){ 61 if(!vis[e[i].v] && e[i].cap>e[i].flow) 62 gao(e[i].v); 63 } 64 } 65 int main(){ 66 while(scanf("%d%d",&M,&N)!=EOF){ 67 int p,u,v,s=0; 68 S=0,T=M+N+1; 69 tot=0,memset(first,-1,sizeof(first)); 70 for(int i=1;i<=M;++i){ 71 scanf("%d",&p); 72 add(0,i,p); 73 s+=p; 74 cin.getline(tools,10000); 75 int len=0,tool; 76 while(sscanf(tools+len,"%d",&tool)==1){ 77 add(i,tool+M,inf); 78 while(tool) tool/=10,len++; 79 len++; 80 } 81 } 82 for(int i=1;i<=N;++i){ 83 scanf("%d",&p); 84 add(i+M,T,p); 85 } 86 s=s-dinic(); 87 memset(vis,0,sizeof(vis)); 88 memset(vis2,0,sizeof(vis2)); 89 gao(S); 90 bool head=1; 91 for(int i=1;i<=M;++i){ 92 if(vis[i]){ 93 if(head){ 94 printf("%d",i); 95 head=0; 96 } 97 else{ 98 printf(" %d",i); 99 } 100 for(int j=first[i];~j;j=e[j].next) 101 vis2[e[j].v-M]=1; 102 } 103 } 104 puts(""); 105 head=1; 106 for(int i=1;i<=N;++i){ 107 if(vis2[i]){ 108 if(head){ 109 printf("%d",i); 110 head=0; 111 } 112 else printf(" %d",i); 113 } 114 } 115 puts(""); 116 cout<<s<<endl; 117 } 118 return 0; 119 }