首页 > 代码库 > POJ 2987 Firing (最大权闭合图,最小割)

POJ 2987 Firing (最大权闭合图,最小割)

http://poj.org/problem?id=2987

Firing
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 7865 Accepted: 2377

Description

You’ve finally got mad at “the world’s most stupid” employees of yours and decided to do some firings. You’re now simply too mad to give response to questions like “Don’t you think it is an even more stupid decision to have signed them?”, yet calm enough to consider the potential profit and loss from firing a good portion of them. While getting rid of an employee will save your wage and bonus expenditure on him, termination of a contract before expiration costs you funds for compensation. If you fire an employee, you also fire all his underlings and the underlings of his underlings and those underlings’ underlings’ underlings… An employee may serve in several departments and his (direct or indirect) underlings in one department may be his boss in another department. Is your firing plan ready now?

Input

The input starts with two integers n (0 < n ≤ 5000) and m (0 ≤ m ≤ 60000) on the same line. Next follows n + m lines. The first n lines of these give the net profit/loss from firing the i-th employee individuallybi (|bi| ≤ 107, 1 ≤ i ≤ n). The remaining m lines each contain two integers i and j (1 ≤ ij ≤ n) meaning the i-th employee has the j-th employee as his direct underling.

Output

Output two integers separated by a single space: the minimum number of employees to fire to achieve the maximum profit, and the maximum profit.

Sample Input

5 5
8
-9
-20
12
-10
1 2
2 5
1 4
3 4
4 5

Sample Output

2 2

Hint

As of the situation described by the sample input, firing employees 4 and 5 will produce a net profit of 2, which is maximum.

Source

POJ Monthly--2006.08.27, frkstyc


网络流。。。好强大。。。orz。。。

题意:

你的公司要裁员,裁掉某个员工可能会获得收益或损失金钱,如果裁掉某个员工,他所有的下属(直接/间接)也要被裁掉,一个员工可能有多个上级和下属,求最大收益及其最少人数。

分析:

点为员工,上级到下级之间连一条有向边,我们最后要求一个图,这个图是封闭的,即没有出边,使得点的权值和(获得/损失)最大,即求最大权闭合图。

最大权闭合图可转化为网络流解,此处仅给出方法,具体证明请参见Amber大神的《最小割模型在信息学竞赛中的应用》。

方法:

1.原图中的有向边的容量设为无穷大;

2.增加源点,连向点权为正的点,容量为点权;

3.增加汇点,点权为负的点连向汇点,容量为点权的绝对值;

4.求解该图的最小割,最大权和为正点权和减最小割;

5.在残留网络中进行深度优先搜索,去掉源点,即为最大权闭合图。


#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cctype>
#include<cmath>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<map>
#include<set>
#define sqr(x) ((x)*(x))
#define LL long long
#define itn int
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
#define eps 1e-10
#define maxm 70007<<1
#define maxn 5007

using namespace std;

const long long INFLL=0x3f3f3f3f3f3f3f3f;

int fir[maxn];
int u[maxm],v[maxm],nex[maxm];
long long cap[maxm],flow[maxm];
int e_max;
int iter[maxn],q[maxn],lv[maxn];

void add_edge(int _u,int _v,long long _w)
{
    int e;
    e=e_max++;
    u[e]=_u;v[e]=_v;cap[e]=_w;
    nex[e]=fir[u[e]];fir[u[e]]=e;
    e=e_max++;
    u[e]=_v;v[e]=_u;cap[e]=0;
    nex[e]=fir[u[e]];fir[u[e]]=e;
}

void dinic_bfs(int s)
{
    int f,r;
    memset(lv,-1,sizeof lv);
    q[f=r=0]=s;
    lv[s]=0;
    while(f<=r)
    {
        int x=q[f++];
        for (int e=fir[x];~e;e=nex[e])
        {
            if (cap[e]>flow[e] && lv[v[e]]<0)
            {
                lv[v[e]]=lv[u[e]]+1;
                q[++r]=v[e];
            }
        }
    }
}

long long dinic_dfs(int _u,int t,long long _f)
{
    if (_u==t)  return _f;
    for (int &e=iter[_u];~e;e=nex[e])
    {
        if (cap[e]>flow[e] && lv[_u]<lv[v[e]])
        {
            long long _d=dinic_dfs(v[e],t,min(_f,cap[e]-flow[e]));
            if (_d>0)
            {
                flow[e]+=_d;
                flow[e^1]-=_d;
                return _d;
            }
        }
    }

    return 0;
}

long long max_flow(int s,int t)
{

    memset(flow,0,sizeof flow);
    long long total_flow=0;

    for (;;)
    {
        dinic_bfs(s);
        if (lv[t]<0)    return total_flow;
        memcpy(iter,fir,sizeof iter);
        long long _f;

        while ((_f=dinic_dfs(s,t,INF))>0)
            total_flow+=_f;
    }

    return total_flow;
}

int cnt=0;
bool vis[maxn];
void dfs(int _u)
{
    cnt++;
    vis[_u]=true;
    for (int e=fir[_u];~e;e=nex[e])
        if (!vis[v[e]] && cap[e]>flow[e])
            dfs(v[e]);
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("/home/fcbruce/文档/code/t","r",stdin);
    #endif // ONLINE_JUDGE

    int n,m,w;
    long long W=0;
    scanf("%d%d",&n,&m);
    int s=0,t=n+1;
    e_max=0;
    memset(fir,-1,sizeof fir);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&w);
        if (w>0)
        {
            W+=w;
            add_edge(s,i,w);
        }
        if (w<0)
        {
            add_edge(i,t,-w);
        }
    }

    int _u,_v;
    for (int i=0;i<m;i++)
    {
        scanf("%d%d",&_u,&_v);
        add_edge(_u,_v,INFLL);
    }

    long long max_profit=W-max_flow(s,t);

    memset(vis,0,sizeof vis);
    dfs(s);

    printf("%d %I64d\n",--cnt,max_profit);



    return 0;
}