「网络流 24 题」太空飞行计划
Posted skylee的OI博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「网络流 24 题」太空飞行计划相关的知识,希望对你有一定的参考价值。
OJ题号:
洛谷2762、LOJ6001、CodeVS1233
题目大意:
有$n$个实验和$m$个仪器,每个实验需要依赖若干个仪器,不同实验可以共享一个仪器。
已知每一个实验$x$,有$p_x$的收益,每一个仪器$y$,有$c_y$的花费。
求最大净收益。
思路:
建立超级源汇$S$和$T$,
对于每一个实验$x$,连一条从$S$到$x$的容量为$p_x$的边;
对于每一个实验$x$对应的每一个仪器$y$,连一条从$x$到$y$的容量为$\infty$的边;
对于每一个仪器$y$连一条从$y$到$T$的容量为$c_y$的边。
这样,如果选择了一个实验,它所依赖的所有仪器都能保证被选中,这样题目就被转化成了一个最大权闭合子图的问题。
答案即为$\displaystyle{\sum_{i=1}^{n}}p_i-$最小割。
对于要输出的方案,可以这样考虑:
一个实验要是能赚钱,必定不会满流,如果满流则说明实验经费全被仪器消耗掉了;
同样,最后一次BFS的时候,对于没有满流的实验,其所需仪器也一定不满流;
最后只要判断Dinic退出时那些实验或仪器是否有被最后最后一次BFS遍历过即可。
细节:
1.读入特别坑,没有告诉你一个实验相关的仪器数量,数据似乎有多余空格,读优改了半个下午,最后的方案是读入时判断这个数前后是否有换行符。
2.以前Dinic写的比较少,都是等Edmonds-Karp算法TLE以后才改成写Dinic,这样就造成我以前写的Dinic虽然都是错的,然而并没有被卡掉,现在才发现每次BFS以后都要对当前弧数组清零,因为上一次增广以后可能会有退流,这就导致原来不能走的边可以走了。
3.这道题最后在洛谷、LOJ上都AC了,然而在CodeVS上却WA了两个点,然后也调不出来,后来发现据说是没有SPJ?
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 const int inf=0x7fffffff; 7 const int EOLN=inf; 8 bool newline; 9 inline int getint() { 10 newline=false; 11 char ch; 12 while(!isdigit(ch=getchar())) { 13 if(ch==‘\n‘) return EOLN; 14 } 15 int x=ch^‘0‘; 16 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘); 17 if(ch==‘\n‘) newline=true; 18 return x; 19 } 20 const int E=5200,V=102; 21 int s,t; 22 struct Edge { 23 int from,to,remain; 24 }; 25 Edge e[E]; 26 int sz=0; 27 std::vector<int> g[V]; 28 inline void add_edge(const int u,const int v,const int w) { 29 e[sz]=(Edge){u,v,w}; 30 g[u].push_back(sz); 31 sz++; 32 } 33 int lev[V]; 34 unsigned cur[V]={0}; 35 inline void bfs() { 36 for(int i=1;i<=t;i++) lev[i]=-1; 37 lev[s]=0; 38 std::queue<int> q; 39 q.push(s); 40 while(!q.empty()) { 41 int x=q.front(); 42 q.pop(); 43 for(unsigned i=0;i<g[x].size();i++) { 44 Edge &y=e[g[x][i]]; 45 if(y.remain&&!~lev[y.to]) { 46 lev[y.to]=lev[x]+1; 47 q.push(y.to); 48 } 49 } 50 } 51 memset(cur,0,sizeof cur); 52 } 53 int dfs(const int x,const int flow) { 54 if(x==t) return flow; 55 for(unsigned &i=cur[x];i<g[x].size();i++) { 56 Edge &y=e[g[x][i]]; 57 if(y.remain&&lev[x]<lev[y.to]) { 58 if(int f=dfs(y.to,std::min(flow,y.remain))) { 59 e[g[x][i]].remain-=f; 60 e[g[x][i]^1].remain+=f; 61 return f; 62 } 63 } 64 } 65 return 0; 66 } 67 inline int Dinic() { 68 int maxflow=0; 69 for(;;) { 70 bfs(); 71 if(!~lev[t]) break; 72 while(int flow=dfs(s,inf)) { 73 maxflow+=flow; 74 } 75 } 76 return maxflow; 77 } 78 int main() { 79 int n=getint(),m=getint(); 80 if(!newline) getint(); 81 s=0,t=n+m+1; 82 int sum=0; 83 for(int i=1;i<=n;i++) { 84 int w=getint(); 85 sum+=w; 86 add_edge(s,i,w); 87 add_edge(i,s,0); 88 if(newline) break; 89 for(int j=getint();j!=EOLN;j=getint()) { 90 add_edge(i,n+j,inf); 91 add_edge(n+j,i,0); 92 if(newline) break; 93 } 94 } 95 for(int i=1;i<=m;i++) { 96 add_edge(n+i,t,getint()); 97 add_edge(t,n+i,0); 98 } 99 int ans=sum-Dinic(); 100 for(int i=1;i<=n;i++) { 101 if(~lev[i]) printf("%d ",i); 102 } 103 puts(""); 104 for(int i=1;i<=m;i++) { 105 if(~lev[n+i]) printf("%d ",i); 106 } 107 puts(""); 108 printf("%d\n",ans); 109 return 0; 110 }
以上是关于「网络流 24 题」太空飞行计划的主要内容,如果未能解决你的问题,请参考以下文章