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;
}

  

 

posted @ 2013-01-23 20:07  E_star  阅读(510)  评论(0编辑  收藏  举报