BZOJ1497 最大获利
题目链接:https://cn.vjudge.net/problem/HYSBZ-1497
知识点: 最小割
解题思路:
将中转站和用户群都视为点,再建立一个源点和一个汇点。
从源点到每个中转站建一条边,容量为该中转站的建立成本,割掉这条边就代表建立了这个中转站,损失了该中转站的建立成本(即该边的容量)。
从每一个用户群到汇点建一条边,容量为该用户群的获益,割掉这条边就代表不满足这个用户群,损失了该用户群的收益(即该边容量)。
对于该用户群所使用的中转站,连一条边到该用户群,容量为 \(INF\),这样就保证了:若要满足该用户群(即保留该用户群到汇点的边),则其所使用的中转站与源点之间的边必须割(代表该用户群所使用的中转站都必须建立);反之则割掉该用户群到汇点的边(即不满足该用户群)。
答案即为所有用户群的总收益减去从源点到汇点的网络流的最小割。
最后介绍一下最大流最小割定理:在一个网络流中,能够从源点到达汇点的最大流量等于如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。即在任何网络中,最大流的值等于最小割的容量。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN=60000; const int MAXM=400000; const int INF=0x3f3f3f3f; struct Edge{ int to,nest,cap,flow; }edge[MAXM]; int head[MAXN],tol; int gap[MAXN],dep[MAXN],cur[MAXN]; void init(){ tol=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int w,int rw=0){ edge[tol].to=v; edge[tol].cap=w; edge[tol].flow=0; edge[tol].nest=head[u]; head[u]=tol++; edge[tol].to=u; edge[tol].cap=rw; edge[tol].flow=0; edge[tol].nest=head[v]; head[v]=tol++; } int Q[MAXN]; void BFS(int start,int ends){ memset(dep,-1,sizeof(dep)); memset(gap,0,sizeof(gap)); gap[0]=1; int fronts=0,rear=0; dep[ends]=0; Q[rear++]=ends; while(fronts!=rear){ int u=Q[fronts++]; for(int i=head[u];i!=-1;i=edge[i].nest){ int v=edge[i].to; if(dep[v]!=-1) continue; Q[rear++]=v; dep[v]=dep[u]+1; gap[dep[v]]++; } } } int S[MAXN]; int sap(int start,int ends,int N){ BFS(start,ends); memcpy(cur,head,sizeof(head)); int top=0; int u=start; int ans=0; while(dep[start]<N){ if(u==ends){ int Min=INF; int inser; for(int i=0;i<top;i++){ if(Min>edge[S[i]].cap-edge[S[i]].flow){ Min=edge[S[i]].cap-edge[S[i]].flow; inser=i; } } for(int i=0;i<top;i++){ edge[S[i]].flow+=Min; edge[S[i]^1].flow-=Min; } ans+=Min; top=inser; u=edge[S[top]^1].to; continue; } bool flag=false; int v; for(int i=cur[u];i!=-1;i=edge[i].nest){ v=edge[i].to; if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u]){ flag=true; cur[u]=i; break; } } if(flag){ S[top++]=cur[u]; u=v; continue; } int Min=N; for(int i=head[u];i!=-1;i=edge[i].nest){ if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min){ Min=dep[edge[i].to]; cur[u]=i; } } gap[dep[u]]--; if(!gap[dep[u]]) return ans; dep[u]=Min+1; gap[dep[u]]++; if(u!=start) u=edge[S[--top]^1].to; } return ans; } int main(){ int N,M,a,b,c; int s=0,t=MAXN-1; init(); scanf("%d%d",&N,&M); for(int i=1;i<=N;i++){ scanf("%d",&c); addedge(s,i,c); } int sum=0; for(int i=1;i<=M;i++){ scanf("%d%d%d",&a,&b,&c); sum+=c; addedge(a,i+N,INF); addedge(b,i+N,INF); addedge(i+N,t,c); } printf("%d\n",sum-sap(s,t,N+M+2)); return 0; }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”