BZOJ 2039: [2009国家集训队]employ人员雇佣 最小割 二元组建图模型

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

Time Limit: 20 Sec  Memory Limit: 259 MB
Submit: 1614  Solved: 789
[Submit][Status][Discuss]

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


最小割心得:
首先需要一定的功底来发现这道题是最小割,并且投入思考。
然后想怎么建图:
最小割都是先算上所有收益,然后再通过网络图进行割边减去部分权值。
收益有时候可能带上负值。
然后我们需要思考什么能带来权值,什么会有权值冲突。
而最小割图一般都是拆成S集和T集考虑,即取与不取,某人/点选A或者选B等等,
这样就会带来冲突,也就是需要割的边。
然后我们需要把所有权值的得与失列出来,针对性建图,然后check几种情况:
比如两个人都在S集、都在T集,甲S乙T,甲T乙S等等,我们看这样会割掉哪些边,失去哪些权值。
如果发现建错了,也可以有针对性地进行修改。


提供一个较有普及型的最小割建图方法




回到这一道题

把自己和敌对公司看成机器A、B,把问题转换为最小花费

即可用最小割解决

那么不选一个点

就代表花费矩中的一行

选一个点就代表花费雇佣该经理的花费

在两个经理之间连E(i,j)<<1的边

代表两经理不在同一公司的增加花费

(<<1是因为两个经理不在同一公司会消耗E(i,j)两次,题意问题)

跑一遍最大流就OK啦


不知道为什么BJ的代码跑的如此之慢QWQ




转载分别来自 (当然 题解部分是BJ自己完成的)

http://blog.csdn.NET/vmurder/article/details/42651751

http://www.cnblogs.com/chenyushuo/p/5146626.html



#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
	return x*f;
}
inline void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=1010,M=3001000,inf=0X3f3f3f3f;

int ecnt=1,last[N];
struct EDGE{int to,nt,val;}e[M];
inline void readd(int u,int v,int val){e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}
inline void add(int u,int v,int val){readd(u,v,val);readd(v,u,0);}

int n,S,T=N-1;

int q[N],d[N];
bool bfs()
{
	memset(d,0,sizeof(d));d[0]=1;
	int head=0,tail=1;q[0]=0;
	while(head<tail)
	{
		int u=q[head++];head%=N;
		for(int i=last[u];i;i=e[i].nt)
		if(e[i].val&&!d[e[i].to])
		{d[e[i].to]=d[u]+1;q[tail++]=e[i].to;tail%=N;}
	}
	return d[T];
}

int dfs(int u,int lim)
{
	if(u==T||!lim)return lim;
	int res=0;
	for(int i=last[u];i;i=e[i].nt)
	if(e[i].val&&d[e[i].to]==d[u]+1)
	{
		int tmp=dfs(e[i].to,min(lim,e[i].val));
		lim-=tmp;e[i].val-=tmp;e[i^1].val+=tmp;res+=tmp;
		if(!lim)break;
	}
	if(!res)d[u]=-1;
	return res;
}

int ans;
void dinic(){while(bfs())ans+=dfs(S,inf);}

int main()
{
	n=read();int sum=0;
	for(int i=1,x;i<=n;++i){x=read();add(i,T,x);}
	for(int i=1,tmp;i<=n;++i)
	{
		tmp=0;
		for(int j=1,x;j<=n;++j)
		{x=read();tmp+=x;if(i!=j)add(i,j,x<<1);}
		sum+=tmp;add(S,i,tmp);
	}
	dinic();
	print(sum-ans);
	return 0;
}
/*
3
3 5 100
0 6 1
6 0 2
1 2 0

1
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值