题目大意:需要解雇若干人,解雇每个人有一个损失或收益,解雇一个人就必须结果他的所有下属,问最大收益。
思路:裸最大权闭合图问题,对于权值为q的点,负权向汇点连-p的边,正权源点向其连p的边,上下级关系上级向下级连oo的边,然后求最大流最小割,最大收益即为总正点权减最小割,需要裁员的人即为残余图中从源点可达的点。
参考:http://blog.csdn.net/scorpiocj/article/details/6085637
#include <bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 #define LB lower_bound #define IINF (1<<29) #define LINF (1ll<<59) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=5010; struct EDGE{ int to,rev; ll cap; EDGE(int t,ll c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,ll cap){ G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int level[maxv]; queue<int> Q; void bfs(int s){ memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(level[e.to]==-1&&e.cap>0){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; ll dfs(int v,int t,ll f){ if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; ll d; if(e.cap>0&&level[e.to]>level[v]){ d=dfs(e.to,t,min(e.cap,f)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } ll dinic(int s,int t){ ll flow=0; for(;;){ bfs(s); if(level[t]==-1){ return flow; } ll f; memset(iter,0,sizeof iter); while((f=dfs(s,t,LINF))>0) flow+=f; } } int cc=0; bool vis[maxv]; void cont(int v){ cc++; vis[v]=1; for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(!vis[e.to]&&e.cap>0){ cont(e.to); } } } const int s=5008,t=5009; int n,m; int pro[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>m; ll sum=0; for(int i=1;i<=n;i++){ ll p; scanf("%lld",&p); pro[i]=p; if(p>0){ addedge(s,i,p); sum+=p; } else addedge(i,t,-p); } for(int i=0;i<m;i++){ ll x,y; scanf("%lld%lld",&x,&y); addedge(x,y,LINF); } ll ans=sum-dinic(s,t); cont(s); cout<<cc-1<<" "<<ans<<endl; }
时间: 2024-11-06 07:04:58