「网络流 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 题」太空飞行计划的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ #6001. 「网络流 24 题」太空飞行计划 最大权闭合图

网络流24题太空飞行计划

[网络流24题]太空飞行计划问题

网络流24题----02太空飞行计划

网络流24题太空飞行计划问题

[网络流24题] 太空飞行计划(cogs 727)