pku 2987 Firing 最大权闭合图 (dinic做)
http://poj.org/problem?id=2987
题意:
公司打算裁员,裁掉某些员工可以获得正收益,而裁掉某些员工会遭受损失。并且员工之间往往存在一定的关系,当某个员工被裁掉之后,在他的关系之下的所有员工都必须被裁掉。现在要求如何裁员才能获得最大收益。
思路:
胡波涛的论文里http://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
各处了解释,证明的最大权闭合图 = 正权点的和 - 最大割(最大流);
关键是如何建图,如果该点的权值为正则由源点向该点建立权值为该点权值的边,如果为负则由该点向汇点建立权值为该点权值的绝对值的边,然后原图中的边权值为正无穷。然后就是求最小割了,这里还要输出裁掉了几个员工。我们只要搜索最大流之后还能和源点连接点的个数即可,因为如果最大流之后还能建立连接说明,裁掉他之后的利益大于损失。。
ps:用了自己的最大流dinic模板死活tle,换了一下别人的就A了,好吧,我的模板搓了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <string>
#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x) ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)
#define ll long long
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 155000
#define N 5007
using namespace std;
//freopen("din.txt","r",stdin);
const __int64 inf=0x3f3f3f3f3f3f3f3fll;
struct node
{
int u,v;
ll w;
int next;
}g[M];
int head[N],ct;
int q[N*100],l,r;
int vt[N];
int out[N];
ll level[N];
ll zw,val[N];
int num;
int n,m;
void add(int u,int v,ll w)
{
g[ct].u = u;
g[ct].v = v;
g[ct].w = w;
g[ct].next = head[u];
head[u] = ct++;
g[ct].u = v;
g[ct].v = u;
g[ct].w = 0;
g[ct].next = head[v];
head[v] = ct++;
}
bool layer(int s,int e)
{
int i;
CL(level,-1);
level[s] = 0;
int l = 0, r= 0;
q[r] = s;
while (l <= r){
int u = q[l++];
for (i = head[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
if (level[v] == -1 && g[i].w > 0)
{
level[v] = level[u] + 1;
q[++r] = v;
if (v == e) return true;
}
}
}
return false;
}
ll dinic(int s,int e){
ll ans = 0;
while (layer(s,e))
{
int top = 0,u = s,i;
for (i = s; i <= e; ++i) out[i] = head[i];
while (out[s] != -1)
{
if (u == e)
{
ll MIN = inf;
for (i = top - 1; i >= 0; --i)
{
MIN = min(MIN,g[q[i]].w);
}
for (i = top - 1; i >= 0; --i)
{
g[q[i]].w -= MIN;
g[q[i]^1].w += MIN;
if (g[q[i]].w == 0) top = i;
}
ans += MIN;
u = g[q[top]].u;
}
else if (out[u] != -1 && g[out[u]].w > 0 && level[u] + 1 == level[g[out[u]].v])
{
q[top++] = out[u];
u = g[out[u]].v;
}
else
{
while (top > 0 && u != s && out[u] == -1)
u = g[q[--top]].u;
out[u] = g[out[u]].next;
}
}
}
return ans;
}
void dfs(int s)
{
vt[s] = 1; num++;
for (int i = head[s]; i != -1; i = g[i].next)
{
int v = g[i].v;
int w = g[i].w;
if (!vt[v] && w > 0) dfs(v);
}
}
void solve(int s,int e)
{
ll tmp = dinic(s,e);//求最小割
CL(vt,0); num = 0;
dfs(s);//求裁员个数
printf("%d %I64d\n",num - 1,zw - tmp);//这里num多加了0
}
int main()
{
//freopen("din.txt","r",stdin);
int i;
int x,y;
while (~scanf("%d%d",&n,&m))
{
CL(head,-1); ct = 0;
//建图
int s = 0, e = n + 1;
for (i = 1; i <= n; ++i)
{
scanf("%I64d",&val[i]);
if (val[i] >= 0)
{
zw += val[i];
add(s,i,val[i]);//点的权值大于0
}
else//小于0
{
add(i,e,-val[i]);
}
}
for (i = 1; i <= m; ++i)
{
scanf("%d%d",&x,&y);
add(x,y,inf);
}
solve(s,e);
}
return 0;
}


浙公网安备 33010602011771号