POJ 2987: Firing

来源:互联网 发布:中国国际形势印度知乎 编辑:程序博客网 时间:2024/06/07 04:06
题目链接:
http://poj.org/problem?id=2987


题意:
现在有一群人,开除每个人都会有一个相应的收益(可正可负)。
另给出他们之间的上下级关系,如果开除一个人就必须开除他的下属,以及他下属的下属,依次类推……
求最大收益的值以及在保证最大收益的前提下最少开除多少人。


算法:

有向图的最大权闭合子图——即闭合图内的任意点的任意后继也一定在闭合图中。

Amber的《最小割模型在信息学竞赛中的应用》里面已经讲解的非常清晰了。

从S到每一个点建一条边,权值为该点权值的绝对值。若该点权值为负,则由该点向T建边,权值为该点权值的绝对值。

若存在边(u, v),则由u 建一条到v 的边,流量为无限大。

最大收益为:正权值点的权值和 - 最小割

S割集为最大权闭合子图中的点,即应该被开除的人。
代码:
#include <cstdio>#include <iostream>#include <algorithm>#include <sstream>#include <cstdlib>#include <cstring>#include <string>#include <climits>#include <cmath>#include <queue>#include <vector>#include <stack>#include <set>#include <map>#define INF 0x3f3f3f3fusing namespace std;const int maxn = 10000;const int maxm = 1000000;int to[maxm], nxt[maxm];long long cap[maxm];int head[maxn], cur[maxn], Q[maxn], dep[maxn];int E;void _addedge(int u, int v, long long w){    to[E] = v;    cap[E] = w;    nxt[E] = head[u];    head[u] = E ++;}void addedge(int u, int v, long long w){    _addedge(u, v, w);    _addedge(v, u, 0);}bool bfs(int S, int T){    memset(dep, -1, sizeof(dep));    dep[T] = 0;    int front = 0, rear = 0;    Q[rear ++] = T;    while (front != rear && dep[S] == -1)    {       int u = Q[front ++];       for (int i = head[u]; i != -1; i = nxt[i])        {            int v = to[i];            if (cap[i ^ 1] && dep[v] == -1)            {                dep[v] = dep[u] + 1;                Q[rear ++] = v;            }        }    }    return dep[S] != -1;}long long dfs(int S, int T, long long lim){    if (S == T)    {        return lim;    }    long long tmp = lim;    for (int &i = cur[S]; i != -1; i = nxt[i])    {        int v = to[i];        if (cap[i] && dep[S] == dep[v] + 1)        {            int ret = dfs(v, T, min(tmp, cap[i]));            tmp -= ret;            cap[i] -= ret;            cap[i ^ 1] += ret;        }        if (!tmp)        {            break;        }    }    return lim - tmp;}long long dinic(int S, int T, int n){    long long ans = 0;    while (bfs(S, T))    {        for (int i = 0; i < n; i ++)        {            cur[i] = head[i];        }        ans += dfs(S, T, INT_MAX);    }    return ans;}void init(){    E = 0;    memset(head, -1, sizeof(head));}int dfs(int u){    int cot = 1;    for (int i = head[u]; i != -1; i = nxt[i])    {        int v = to[i];        if (dep[v] == -1 && cap[i])        {            dep[v] = dep[u] + 1;            cot += dfs(v);        }    }    return cot;}int main(){    int n, m;    while (scanf("%d %d", &n, &m) == 2)    {        long long sum = 0;        init();        int S = 0, T = n + 1;        for (int i = 1; i <= n; i ++)        {            long long bonus;            scanf("%lld", &bonus);            if (bonus > 0)            {                addedge(S, i, bonus);                sum += bonus;            }            else            {                addedge(i, T, -bonus);            }        }        while (m --)        {            int u, v;            scanf("%d %d", &u, &v);            addedge(u, v, INF);        }        long long flow = dinic(S, T, n + 2);        memset(dep, -1, sizeof(dep));        dep[0] = 0;        printf("%d %lld\n", dfs(0) - 1, sum - flow);    }    return 0;}


0 0