P1791-[國家集訓隊]人員僱傭【最大權閉合圖】
阿新 • • 發佈:2021-07-15
正題
題目連結:https://www.luogu.com.cn/problem/P1791
題目大意
有\(n\)個人,僱傭第\(i\)個需要\(A_i\)的費用,對於\(E_{i,j}\)表示如果\(i\)選了的話,選擇\(j\)會獲得\(E_{i,j}\)的費用,不選\(j\)會花費\(E_{i,j}\)的費用。
\(1\leq n\leq 1000\)
解題思路
考慮網最大權值閉合圖,先加上所有可以獲得的權值,然後考慮需要失去的最小權值。
因為每個人可以選或者不選,那麼就可以讓\(s\)連線\(i\)且\(i\)連線\(t\)這樣兩邊必須割掉一條表示選擇或者不選擇。
考慮讓\(s->i\)
\(i->t\)表示不選擇那麼所有由\(i\)產生的費用都不能獲得,權值為\(\sum_{j=1}^mE_{i,j}\)。
然後對於一個\(E_{i,j}\)如果\(i\)選擇了且\(j\)沒有選擇那麼就會失去\(2\times E_{i,j}\)的流量,在\(i\)和\(j\)之間連一條\(2\times E_{i,j}\)的就好了。
code
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define ll long long using namespace std; const ll N=2e4+10,inf=2147483647; struct node{ ll to,next,w; }a[N*4]; ll n,s,t,tot,cnt,A[N],ls[N],dep[N],ans; queue<int> q; void addl(ll x,ll y,ll w){ a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w; a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0; return; } bool bfs(){ memset(dep,0,sizeof(dep));dep[s]=1; while(!q.empty())q.pop();q.push(s); while(!q.empty()){ ll x=q.front();q.pop(); for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to; if(dep[y]||!a[i].w)continue; dep[y]=dep[x]+1; if(y==t)return 1; q.push(y); } } return 0; } ll dinic(ll x,ll flow){ if(x==t)return flow; ll rest=0,k; for(ll i=ls[x];i;i=a[i].next){ ll y=a[i].to; if(dep[y]!=dep[x]+1||!a[i].w)continue; rest+=(k=dinic(y,min(a[i].w,flow-rest))); a[i].w-=k;a[i^1].w+=k; if(rest==flow)return flow; } if(!rest)dep[x]=0; return rest; } signed main() { scanf("%lld",&n); s=n+1;t=s+1;tot=1; for(ll i=1;i<=n;i++){ ll x; scanf("%lld",&x); addl(s,i,x); } for(ll i=1;i<=n;i++){ ll S=0; for(ll j=1;j<=n;j++){ ll x;scanf("%lld",&x); if(!x)continue; S+=x;addl(i,j,2*x); } addl(i,t,S);ans+=S; } while(bfs()) ans-=dinic(s,inf); printf("%lld\n",ans); return 0; }