正题
题目链接:https://www.luogu.com.cn/problem/P1791
题目大意
有nnn个人,雇佣第iii个需要AiA_iAi的费用,对于Ei,jE_{i,j}Ei,j表示如果iii选了的话,选择jjj会获得Ei,jE_{i,j}Ei,j的费用,不选jjj会花费Ei,jE_{i,j}Ei,j的费用。
1≤n≤10001\leq n\leq 10001≤n≤1000
解题思路
考虑网最大权值闭合图,先加上所有可以获得的权值,然后考虑需要失去的最小权值。
因为每个人可以选或者不选,那么就可以让sss连接iii且iii连接ttt这样两边必须割掉一条表示选择或者不选择。
考虑让s−>is->is−>i表示选择,那么s−>is->is−>i权值为AiA_iAi。
i−>ti->ti−>t表示不选择那么所有由iii产生的费用都不能获得,权值为∑j=1mEi,j\sum_{j=1}^mE_{i,j}∑j=1mEi,j。
然后对于一个Ei,jE_{i,j}Ei,j如果iii选择了且jjj没有选择那么就会失去2×Ei,j2\times E_{i,j}2×Ei,j的流量,在iii和jjj之间连一条2×Ei,j2\times E_{i,j}2×Ei,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;
}