题解 P1791 【[国家集训队]人员雇佣】
nianheng
2018-07-14 11:03:18
~~竟然没有这题的题解,水一发qwq~~
## 最小割模型
#### 1.题意:
a.选择每个人有一个代价$A_i$;
b.如果有两个人同时选择就可以获得收益$E_{i,j}$
c.如果一个人选择另一个不选会产生代价$E_{i,j}$
#### 2.解法
a,b两条就是典型的最小割模型,从$S$向每个人连边流量为这人可以带来的收益$\Sigma_{j=1}^{n} E_{i,j}$,用$ans$记录总收益,再从人向$T$连边表示代价$A_i$,用$ans-minc$(这样如果割掉人到$T$的边代表选这个人并支付代价,如果割掉$S$到人的边代表舍弃收益,不支付代价)得到的就是最大的收益
考虑c条件,那么我们对于每对$i,j$都再新建一条从$i$到$j$的边,流量为$E_{i,j}<<1$这样如果选一个不选另一个的话这条边就会断掉(造成代价),c条件就能满足了
最小割可以转化为最大流,我用的$dinic$极限数据$942ms$~~有点慌~~
~~还有就是千万别忘开long long~~
#### 3.代码
```cpp
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
inline long long read(){
long long ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
return ans*fh;
}
const int maxn=1e4,maxm=5e6,inf=0x7fffffff;
int n,m,a,b,c,cc[maxn],cur[maxn],head[maxn],nex[maxm],v[maxm],s,t;
int num=1;
long long ans,siz[maxn],fee,w[maxm];
queue<int>q;
int bh(int x,int y){return n+(x-1)*n+y;}
void add(int x,int y,long long z){
v[++num]=y;
w[num]=z;
nex[num]=head[x];
head[x]=num;
v[++num]=x;
w[num]=0;
nex[num]=head[y];
head[y]=num;
}
bool bfs(){
memset(cc,0,sizeof(cc));
cc[s]=1;
q.push(s);
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=head[now];i;i=nex[i])
if(w[i]&&!cc[v[i]])
cc[v[i]]=cc[now]+1,q.push(v[i]);
}
return cc[t];
}
long long dfs(int x,long long ll){
if(x==t)
return ll;
for(int &i=cur[x];i;i=nex[i])
if(w[i]&&cc[v[i]]==cc[x]+1){
int pp=dfs(v[i],ll>w[i]?w[i]:ll);
if(pp){
w[i]-=pp;
w[i^1]+=pp;
return pp;
}
}
return 0;
}
void dinic(){
long long maxl=0,ll;
while(bfs()){
memcpy(cur,head,sizeof(cur));
while(ll=dfs(s,inf))
maxl+=ll;
}
printf("%d\n",ans-maxl);
}
int main(){
n=read();s=0;t=n+1;
for(int i=1;i<=n;i++)
a=read(),add(i,t,a);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
fee=read();
siz[i]+=fee;
add(i,j,fee*2);
}
add(s,i,siz[i]);
ans+=siz[i];
}
dinic();
return 0;
}
```