[网络流24题] 太空飞行计划问题 (最大流->最大权闭合图)

洛谷传送门 LOJ传送门

做这道题之前建议先看这篇论文,虽然论文里很多地方用了很多术语,但hbt神犇讲得很明白

这篇题解更加偏向于感性理解

把问题放到二分图上,左侧一列点是实验,权值为$p[i]$,右侧一列点是仪器,权值为$c[i]$,左侧向右侧连接了许多条出边

如果想获得$p[i]$,需要保证i的所有出边都被选上了

按照论文里最大权闭合图的做法,实验和源点$s$相连,流量为$p[i]$,仪器和汇点$t$相连,流量为$c[i]$,实验和仪器之间的边流量为$inf$

最终的答案就是实验带来的报酬总和-这个图的最大流,即报酬总和去掉 报酬填补费用的总和 ,就是净收益

我们如何比较感性地理解它呢

求最大流的过程,可以看成用实验的钱消去仪器的钱的过程

而有一些实验可能先被遍历到,而这次实验的报酬小于所需仪器的总花费,即这次实验有点亏,但买了给其他实验用的仪器,而其他实验可以把这些钱补回来

在图上的体现就是,有其他实验的流量沿着反向边流向了这次实验

通过求最小割,我们把图分割成了两块,一块包含源点,一块包含汇点

源点指向的每个联通块中,都至少存在一个点,源点向它的流量$>0$,那么这个联通块里的实验仪器组合才是获利的

那么最终答案就是总获利-最大流,方案就是再进行一次$bfs$,$dep$有值的点,即和源点在同一联通块里的点。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #define N1 350
 6 #define M1 3010
 7 #define ll long long
 8 #define dd double
 9 #define inf 0x3f3f3f3f
10 using namespace std;
11 
12 int gint()
13 {
14     int ret=0,fh=1;char c=getchar();
15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
17     return ret*fh;
18 }
19 
20 int n,m,nm,S,T;
21 int p[N1],c[N1];  
22 struct Edge{
23 int head[N1],to[M1<<1],nxt[M1<<1],flow[M1<<1],cte;
24 void ae(int u,int v,int F)
25 {
26     cte++; to[cte]=v; flow[cte]=F;  
27     nxt[cte]=head[u]; head[u]=cte;
28 }
29 }e;
30 
31 int que[M1],hd,tl,dep[N1],cur[N1];
32 int bfs(Edge &E)
33 {
34     int x,j,v;
35     memset(dep,-1,sizeof(dep)); memcpy(cur,E.head,sizeof(cur));
36     hd=1,tl=0; que[++tl]=S; dep[S]=0; 
37     while(hd<=tl)
38     {
39         x=que[hd++];
40         for(j=E.head[x];j;j=E.nxt[j])
41         {
42             v=E.to[j]; if(dep[v]!=-1||!E.flow[j]) continue;
43             dep[v]=dep[x]+1; que[++tl]=v;
44         }
45     }
46     return dep[T]!=-1;
47 }
48 int dfs(Edge &E,int x,int limit)
49 {
50     int j,v,flow,ans=0; if(x==T||!limit) return limit;
51     for(j=cur[x];j;j=E.nxt[j])
52     {
53         cur[x]=j; v=E.to[j];
54         if( dep[v]==dep[x]+1 && (flow=dfs(E,v,min(E.flow[j],limit))) )
55         {
56             limit-=flow; ans+=flow;
57             E.flow[j]-=flow; E.flow[j^1]+=flow;
58             if(!limit) break;
59         }
60     }
61     return ans;
62 }
63 int use[N1],de;
64 void Dinic()
65 {
66     int mxflow=0,x,j,v,i,flag,sum=0;
67     for(i=1;i<=m;i++) sum+=p[i];
68     while(bfs(e)) mxflow+=dfs(e,S,inf);
69     bfs(e);
70     for(i=1;i<=m;i++) if(dep[i]!=-1) printf("%d ",i); puts("");
71     for(i=m+1;i<=m+n;i++) if(dep[i]!=-1) printf("%d ",i-m); puts("");
72     printf("%d\n",sum-mxflow);
73 }
74 
75 char str[1000];
76 int main()
77 {
78     scanf("%d%d",&m,&n);
79     int i,j,x,y,flag,slen; S=m+n+1,T=m+n+2; e.cte=1;
80     for(i=1,slen=0;i<=m;i++)
81     {
82         p[i]=gint(); e.ae(S,i,p[i]), e.ae(i,S,0);
83         memset(str,0,sizeof(str)); cin.getline(str,1000); slen=0;
84         while(sscanf(str+slen,"%d",&x)==1)
85         {
86             e.ae(i,x+m,inf); e.ae(x+m,i,0); 
87             if(!x) slen++;
88             else{ while(x) x/=10,slen++;}
89             slen++;
90         }
91     }
92     for(i=m+1;i<=m+n;i++) c[i]=gint(), e.ae(i,T,c[i]), e.ae(T,i,0);
93     Dinic();
94     return 0;
95 }

 

posted @ 2019-01-19 08:43  guapisolo  阅读(267)  评论(0编辑  收藏  举报