poj2987 Firing 最大权闭合图 (最大流)

       题意是一个公司要裁员,每个员工有一个权值表示裁掉他可以给公司带来多少收益,员工之间存在下属关系,下属关系具有传递性,并且可能会存在环,裁掉一个人后,他的直接,间接下属都会被裁掉,求裁员的最大收益和获得该收益最少要裁掉的人数。

       注意到删除的是一个闭合子图,实质上就是求一个最大权闭合图了,新建一个原点s汇点t,节点价值大于0则连一条s指向该节点的边,否则连一条指向t的边,原图中的边则转换成网络中节点相同,容量无穷大的边,这里有个结论,最大权=正权值之和-最小割,那么实际上就是求这个网络的最小割了,证明网上有很多就不赘述了。之后对残余网络做一遍dfs,找一下s可以走到的节点数就是最少要裁掉的人数了。

        

/*=============================================================================
#  Author:Erich
#  FileName:
=============================================================================*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#define lson id<<1,l,m
#define rson id<<1|1,m+1,r

using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=1ll<<60;
const double PI=acos(-1.0);
int n,m,k;
const int maxn=6060;
struct Edge
{
	int u,v,next;
	ll flow,cap;
}edge[202000];
int g[maxn];
int en;
bool vis[maxn];
int d[maxn];
int cur[maxn];
int s,t;
void addedge(int x,int y,int z)
{
	edge[en].u=x;
	edge[en].v=y;
	edge[en].cap=z;
	edge[en].flow=0;
	edge[en].next=g[x];
	g[x]=en++;

	edge[en].u=y;
	edge[en].v=x;
	edge[en].cap=edge[en].flow=0;
	edge[en].next=g[y];
	g[y]=en++;
}
int w[maxn];
bool bfs()
{
	memset(vis,0,sizeof vis);
	queue<int> q;
	q.push(s);
	d[s]=0;
	vis[s]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for (int j=g[x]; j!=-1; j=edge[j].next)
		{
			int v=edge[j].v;
			if (!vis[v] && edge[j].cap>edge[j].flow)
			{
				vis[v]=1;
				d[v]=d[x]+1;
				q.push(v);
			}
		}
	}
	return vis[t];
}
ll dfs(int x,ll a)
{
	if (x==t || a==0) return a;
	ll flow=0,f;
	for (int &j=cur[x]; j!=-1; j=edge[j].next)
	{
		int v=edge[j].v;
		if (d[x]+1==d[v] && (f=dfs(v,min(a,edge[j].cap-edge[j].flow)))>0)
		{
			edge[j].flow+=f;
			edge[j^1].flow-=f;
			flow+=f;
			a-=f;
			if (a==0) break;
		}
	}
	return flow;
}
ll Maxflow(int s,int t)
{
	memset(d,0,sizeof d);
	ll flow=0;
	while(bfs())
	{
		for (int i=0; i<=n+1; i++)
			cur[i]=g[i];
		flow+=dfs(s,inf);
	}
	return flow;
}

void dfs2(int u)
{
	vis[u]=true;
	for (int j=g[u]; j!=-1; j=edge[j].next)
	{
		int v=edge[j].v;
		if (edge[j].cap>edge[j].flow && !vis[v])
		{
			dfs2(v);
		}
	}
}
int main()
{
//	freopen("in.txt","r",stdin);

	while(~scanf("%d%d",&n,&m))
	{
		memset(g,-1,sizeof g);
		en=0;
		ll sum=0;
		for (int i=1; i<=n; i++) 
		{
			scanf("%d",&w[i]);
			if (w[i]>0) sum+=w[i];
		}
		int x,y;
		for (int i=0; i<m; i++)
		{
			scanf("%d%d",&x,&y);
			addedge(x,y,inf);
		}
		s=0;
		t=n+1;
		for (int i=1; i<=n; i++)
		{
			x=w[i];
			if (x>0) addedge(s,i,x);
			else addedge(i,t,-x);
		}
		ll ans=Maxflow(s,t);
		ans=sum-ans;
		memset(vis,false,sizeof vis);
		dfs2(s);
		int ans2=0;
		for (int i=1; i<=n; i++)
			if (vis[i]) ans2++;
		cout<<ans2<<" "<<ans<<endl;
	}


	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值