您的位置:首页 > 其它

2039: [2009国家集训队]employ人员雇佣

2017-09-14 07:32 381 查看
题目链接

题目大意:给定n个人,每个人有一个佣金,i和j如果同时被雇佣会产生2*E(i,j)的效益,i和j如果一个被雇佣一个不被雇佣会产生E(i,j)的亏损,求最大收益

题解:转化成最小割,和S相连表示不选,和T相连表示选取,割表示损失的部分

对于雇佣的人i和T相连,就要和S割断,所以S向i连容量为雇佣费用的边

对于雇佣的人i和S相连,就要和T割断,损失∑E[i][j],向T连边

对于一对点i,j,连边(i,j,2*E[i][j]),表示如果不选i,选了j的话,本来i中选j的利益得不到,又要损失j对i的影响为E[i][j],一共损失了2*E[i][j]

任意点i到T连一条容量为Σ(j=1,n)E[i,j]的边

任意两点i,j间连一条容量为2*E[i,j]的双向边

任意点i到S连一条边,容量为雇佣的费用

再次Orz神奇题解

我的收获:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int M=1005;
#define INF 1e15

int n,m,st,ed,t;
int head[M],last[M],d[M],num[M];
long long sum;
bool Exit;

struct edge{int to,nex;long long c;}e[M*M*2];

void add(int u,int v,long long w){e[t].to=v,e[t].nex=head[u],e[t].c=w,last[u]=head[u]=t++;}
void insert(int x,int y,long long z){add(x,y,z),add(y,x,0);};

long long dfs(int x,long long in)
{
if(x==ed) return in;
long long ans=0,f;
for(int i=last[x];i!=-1;last[x]=i=e[i].nex)
{
int v=e[i].to;
if(e[i].c&&d[v]==d[x]-1){
f=dfs(v,min(in-ans,e[i].c));
ans+=f;e[i].c-=f,e[i^1].c+=f;
if(Exit||ans==in) return ans;
}
}
if(--num[d[x]]==0) Exit=1;
d[x]++,num[d[x]]++,last[x]=head[x];
return ans;
}

long long ISAP()
{
Exit=0;long long flow=0;
while(!Exit) flow+=dfs(st,INF);
return flow;
}

void work()
{
cout<<sum-ISAP()<<endl;
}

void init()
{
cin>>n;
st=0,ed=n+1,num[0]=ed+1;
memset(head,-1,sizeof(head));
memset(last,-1,sizeof(last));
long long x,resi;
for(int i=1;i<=n;i++)
scanf("%lld",&x),insert(st,i,x);
for(int i=1;i<=n;i++){
resi=0;
for(int j=1;j<=n;j++)
{
scanf("%lld",&x);
sum+=x,resi+=x;
insert(i,j,x<<1);
}
insert(i,ed,resi);
}
}

int main()
{
init();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: