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;

}

  

 

你可能感兴趣的:(dinic)