[题解]bzoj2039(2009国家集训队)employ人员雇佣

Description

作为一个富有经营头脑的富翁,小L决定从本国最优秀的经理中雇佣一些来经营自己的公司。这些经理相互之间合作有一个贡献指数,(我们用Ei,j表示i经理对j经理的了解程度),即当经理i和经理j同时被雇佣时,经理i会对经理j做出贡献,使得所赚得的利润增加Ei,j。当然,雇佣每一个经理都需要花费一定的金钱Ai,对于一些经理可能他做出的贡献不值得他的花费,那么作为一个聪明的人,小L当然不会雇佣他。 然而,那些没有被雇佣的人会被竞争对手所雇佣,这个时候那些人会对你雇佣的经理的工作造成影响,使得所赚得的利润减少Ei,j(注意:这里的Ei,j与上面的Ei,j 是同一个)。 作为一个效率优先的人,小L想雇佣一些人使得净利润最大。你可以帮助小L解决这个问题吗?

Input

第一行有一个整数N<=1000表示经理的个数 第二行有N个整数Ai表示雇佣每个经理需要花费的金钱 接下来的N行中一行包含N个数,表示Ei,j,即经理i对经理j的了解程度。(输入满足Ei,j=Ej,i)

Output

第一行包含一个整数,即所求出的最大值。

Sample Input

3
3 5 100
0 6 1
6 0 2
1 2 0

Sample Output

1

【数据规模和约定】
20%的数据中N<=10
50%的数据中N<=100
100%的数据中 N<=1000, Ei,j<=maxlongint, Ai<=maxlongint

Solution

        网络流最小割。构图首先按照惯例,S向每个人连这个人所能造成的所有的价值(雇佣所有人的情况),每个人向T连雇佣他的花费。关键问题是如何处理经理相互影响的状况。当两个经理一个选一个不选的时候,我们不仅要割掉一个经理的价值和另一个经理的雇佣费用,还需要承受两个经理之间的影响后果。相当于我们不但不能获得E[i,j]的收益,还要倒扣E[i,j]的收益,相当于割掉一条边权为2*E[i,j]的边。所以我们这么构图:

然后跑一边最大流,用所有人都雇佣的最大收益减去最大流就得到答案。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
using namespace std;
 
inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
typedef long long LL;
const int maxn=1010;
struct edge{
    int to,next,same;
    LL size;
}e[maxn*maxn*3];
const LL oo=LONG_LONG_MAX;
int E[maxn][maxn],n,a[maxn],num=0,head[maxn];
int que[maxn],dep[maxn],cur[maxn];
LL sum[maxn],ans=0;
 
void adde(int u,int v,LL w){
    e[++num].to=v;e[num].size=w;
    e[num].next=head[u];
    head[u]=num;
}
void add(int u,int v,LL w){
    adde(u,v,w);e[num].same=num+1;
    adde(v,u,0);e[num].same=num-1;
}
bool bfs(){
    memset(que,0,sizeof que);
    memset(dep,0,sizeof dep);
    int qhead=0,tail=1;
    que[1]=0;dep[0]=1;
    while(qhead<tail){
        int now=que[++qhead];
        for(int i=head[now];i;i=e[i].next){
            if(!dep[e[i].to]&&e[i].size){
                que[++tail]=e[i].to;
                dep[e[i].to]=dep[now]+1;
                if(e[i].to==n+1)return true;
            }
        }
    }
    return false;
}
LL dfs(int x,LL flow){
    if(x==n+1||(!flow))return flow;
    LL temp;
    for(int &i=cur[x];i;i=e[i].next){
        if(dep[e[i].to]==dep[x]+1&&(temp=dfs(e[i].to,min(flow,e[i].size)))){
            e[i].size-=temp;
            e[e[i].same].size+=temp;
            return temp;
        }
    }
    return 0;
}
void Dinic(){
    while(bfs()){
        int flow;
        memcpy(cur,head,sizeof head);
        while((flow=dfs(0,oo))){
            ans-=flow;
        }
    }
}
 
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        add(i,n+1,a[i]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            E[i][j]=read();
            if(i!=j)add(i,j,E[i][j]<<1);
            sum[i]+=E[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        add(0,i,sum[i]);
        ans+=sum[i];
    }
    Dinic();
    printf("%lld\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值