BZOJ1497 NOI2006 最大获利 网络流
题意:给定N个用户和M个基站,每个基站有建设费用,每个用户由(a,b,c)描述,表示a b基站都建好后能获得c的收益,求最大获利
题解:
然后说一下我的理解,我么按如下方式建图:每条边看作一个点,然后划分成二分图,左边一列是原图中的点也就是发送站,右边一列是原图中的边抽象成的点,然后建立原点s和汇点t,s向左边的所有点连容量为p[i]的边,然后右边的所有点向t连容量为c[j]的边,如果原图中一条边e连接点u v(e产生收益,前提是u v均已被选择),那么在二分图中将左边的u v分别和右边的e相连,容量为无穷大。
然后解释为何这样构图,根据最小割的性质,一条无穷大的边一定不会在最小割中,因此最小割中的边要么起点是s,要么终点是v
由于:净收益=(总收益-花费)
因此:最大净收益=全部收益-(最小收益+最小花费)
也就是说我们要让花费与没有被利用的边的边权和的和最小(也就是建造发送站的花费和不能产生收益的边的收益的和最小)。
实际上我们的决策过程就是在判定每个发送站是否需要建造。如果删掉s出发的指向u的边,就代表不建造u这个发送站;同时由于割的性质,必须要删除以u为基础的收益的那个点连向t的边才可以。因为建好的图中s出发的边代表花费,而连向t的边代表收益,因而这样跑出的最小割就是(最小总收益+最小花费)。说白了这就是个补集的思想,既然求不出最大的就先求最小的,然后用总的减去最小的不就是最大了的吧。
#include <queue> #include <vector> #include <cstdio> #include <climits> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int MAXN=50000+2; const int MAXM=6000000+2; struct EDGE{ int u,c; EDGE(){} EDGE(int _u,int _c):u(_u),c(_c){} }e[MAXM]; int N,M,cnt,d[MAXN],cur[MAXN],sum; queue<int> q; vector<int> tab[MAXN]; void Insert(int u,int v,int c){ tab[u].push_back(cnt),e[cnt++]=EDGE(v,c); tab[v].push_back(cnt),e[cnt++]=EDGE(u,0); } bool BFS(int s,int t){ memset(d,-1,sizeof(d)); d[s]=0,q.push(s); int x; while(!q.empty()){ x=q.front(),q.pop(); for(int i=0;i<tab[x].size();i++) if(d[e[tab[x][i]].u]==-1 && e[tab[x][i]].c) d[e[tab[x][i]].u]=d[x]+1,q.push(e[tab[x][i]].u); } return d[t]>0; } int DFS(int x,int f,int t){ if(x==t) return f; int flow,used=0; for(int i=cur[x];i<tab[x].size();i++) if(e[tab[x][i]].c && d[e[tab[x][i]].u]==d[x]+1){ flow=DFS(e[tab[x][i]].u,min(f-used,e[tab[x][i]].c),t); e[tab[x][i]].c-=flow,e[tab[x][i]^1].c+=flow,used+=flow; if(e[tab[x][i]].c) cur[x]=i; if(f==used) return f; } if(!used) d[x]=-1; return used; } int Dinic(int s,int t){ int ret=0; while(BFS(s,t)){ memset(cur,0,sizeof(cur)); ret+=DFS(s,INT_MAX,t); } return ret; } int main(){ scanf("%d %d",&N,&M); for(int i=1,p;i<=N;i++){ scanf("%d",&p); Insert(0,i,p); } for(int i=1,a,b,c;i<=M;i++){ scanf("%d %d %d",&a,&b,&c); sum+=c; Insert(a,i+N,INT_MAX),Insert(b,i+N,INT_MAX),Insert(i+N,N+M+1,c); } printf("%d\n",sum-Dinic(0,N+M+1)); return 0; }