1. 程式人生 > >POJ2987:Firing——題解

POJ2987:Firing——題解

break main 能夠 ash bre bfs typedef pac type

http://poj.org/problem?id=2987

題目大意:

炒掉一個人能夠獲得b收益(b可以<0),但是炒掉一個人必須得炒掉他的下屬(然後繼續遞歸)。

求最大收益和此時最小裁員。

——————————————————————————————

我們需要用到最大權閉合圖的知識。

為什麽呢?我們把u是v的上司變成u—>v,那麽我們要求的就是當前圖的最大權閉合圖。

那麽按照最大權閉合圖的知識建立圖跑最大流即可。

如何求最大權閉合圖:

先在原圖中添加s(源點)和t(匯點),原圖中的邊權全部設為INF,權值為正的點與s連邊,權值為點的權值,為負則與t連邊,權值為負的點的權值。

答案為原圖中權值為正的點的和 - 最小割(最大流)

(以下證明不一定嚴謹)

然後考慮多少人,我們發現原來減掉的最小割就可以表示成我們沒有裁的人的權值,那麽沒有裁的人必然流滿了。

所以我們裁掉的人就是我們最小割沒有流滿的人,dfs一下即可。

//不開long long見祖宗,十年OI一場空。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int INF=2147483640;
const int maxn=5010;
inline int read(){
    int X=0,w=0
;char ch=0; while(!isdigit(ch)){w|=ch==-;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int S,T; struct node{ int nxt; int to; ll w; }edge[60000*2+maxn*2+1]; int head[maxn],cnt=-1; void add(int u,int v,int w){ cnt++; edge[cnt].to=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt; return; } int lev[maxn],cur[maxn]; bool bfs(int m){ int dui[m],r=0; for(int i=1;i<=m;i++){ lev[i]=-1; cur[i]=head[i]; } dui[0]=S,lev[S]=0; int u,v; for(int l=0;l<=r;l++){ u=dui[l]; for(int e=head[u];e!=-1;e=edge[e].nxt){ v=edge[e].to; if(edge[e].w>0&&lev[v]==-1){ lev[v]=lev[u]+1; r++; dui[r]=v; if(v==T)return 1; } } } return 0; } ll dinic(int u,int flow,int m){ if(u==m)return flow; ll res=0,delta; for(int &e=cur[u];e!=-1;e=edge[e].nxt){ int v=edge[e].to; if(edge[e].w>0&&lev[u]<lev[v]){ delta=dinic(v,min(edge[e].w,flow-res),m); if(delta>0){ edge[e].w-=delta; edge[e^1].w+=delta; res+=delta; if(res==flow)break; } } } if(res!=flow)lev[u]=-1; return res; } bool vis[maxn]; int dfs(int u){ int ans=1; vis[u]=1; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(edge[i].w>0&&!vis[v]){ ans+=dfs(v); } } return ans; } int main(){ memset(head,-1,sizeof(head)); int n=read(); int m=read(); S=n+1;T=n+2; ll sum=0; for(int i=1;i<=n;i++){ ll b=read(); if(b>0){ sum+=b; add(S,i,b); add(i,S,0); }else{ add(i,T,-b); add(T,i,0); } } for(int i=1;i<=m;i++){ int a=read(); int b=read(); add(a,b,INF); add(b,a,0); } ll ans=0; while(bfs(T))ans+=dinic(S,INF,T); printf("%d %lld\n",dfs(S)-1,sum-ans); return 0; }

POJ2987:Firing——題解