[BZOJ] 1497: [NOI2006]最大获利
点上带权,选A则必须选B,典型的最大权闭合子图问题。
S向正权点(用户)连容量为权值的边,负权点(中转站)向T连容量为权值相反数的边,用户向依赖的中转站连INF的边。
求出最小割,可以这样理解。
要不割去用户的边,代表损失这些收入,要不割去中转站的边,代表加上这些代价。
最后用总收入(正权和)减去最小割即为所求。
#include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; const int MAXN=55005; const int M=50005*9; const int INF=1<<30; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } struct Edge{ int next,to,f; }e[M<<1]; int ecnt=1,head[MAXN]; inline void add(int x,int y,int f){ e[++ecnt].next = head[x]; e[ecnt].to = y; e[ecnt].f = f; head[x] = ecnt; } int n,m; int S,T; int dep[MAXN]; queue<int> Q; bool bfs(int s,int t){ memset(dep,0,sizeof(dep)); dep[s]=1;Q.push(s); while(!Q.empty()){ int top=Q.front();Q.pop(); for(int i=head[top];i;i=e[i].next){ int v=e[i].to; if(dep[v]||!e[i].f) continue; dep[v]=dep[top]+1;Q.push(v); } } return dep[t]; } int cur[MAXN]; int dfs(int x,int flow,int t){ if(x==t) return flow; int used=0,tmp; for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(dep[v]!=dep[x]+1) continue; tmp=dfs(v,min(flow-used,e[i].f),t); e[i].f-=tmp;e[i^1].f+=tmp;used+=tmp; if(used==flow) return flow; if(e[i].f) cur[x]=i; } if(!used) dep[x]=-1; return used; } int dinic(int s,int t){ int ret=0; while(bfs(s,t)){ memcpy(cur,head,sizeof(head)); ret+=dfs(s,INF,t); } return ret; } int main(){ n=rd();m=rd(); S=n+m+1,T=n+m+2; int x,y,f; for(int i=1;i<=n;i++){ x=rd();add(i+m,T,x);add(T,i+m,0); } int ans=0; for(int i=1;i<=m;i++){ x=rd();y=rd();f=rd();ans+=f; add(i,x+m,INF);add(x+m,i,0); add(i,y+m,INF);add(y+m,i,0); add(S,i,f);add(i,S,0); } ans-=dinic(S,T); printf("%d\n",ans); return 0; }
本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9496516.html