POJ_2987_Firing(最大权闭合图)

题型:网络流


题意:

      公司裁员,裁掉一个员工,会赚入或亏损。员工之间用上下级关系,裁掉一个员工,就要裁掉他的下级。要求裁员后赚钱最多,求裁掉的员工数和最多的收入。


分析:

      采用最大权闭合图来建模,利用最大流求解。

      源点与正权员工建边,权值为裁掉该员工可获得的收入;

      上下级建边,权值为inf;

      负权员工与汇点建边,权值为裁掉该员工的亏损值;

      最多收入等于正权值之和减去最大流。

      跑完最大流之和,从源点S开始dfs残留网络,有剩余流量的边所属的点,即为被裁掉的员工。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>

#define LL long long
#define mt(a,b) memset(a,b,sizeof(a))
#define PI acos(-1.0)
using namespace std;
LL value[5010];

const LL inf=0x3f3f3f3f3f3f3f3fLL;
class Dinic { ///最大流(MV^2*ME)
    typedef LL typef;///流量的类型
    static const int ME=1000000;///边的个数
    static const int MV=5010;///点的个数
    int temp[MV],cur[MV],level[MV],path[MV];
    bool used[MV];
    queue<int> q;
    typef flow;
    bool bfs(int s,int t) {
        mt(level,-1);
        while(!q.empty()) q.pop();
        q.push(s);
        level[s]=1;
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=g.head[u]; ~i; i=g.e[i].next) {
                int v=g.e[i].v;
                if(level[v]==-1&&g.e[i].flow) {
                    level[v]=level[u]+1;
                    q.push(v);
                    if(v==t) return true;
                }
            }
        }
        return false;
    }

public:
    struct G {
        struct E {
            int u,v,next;
            typef flow;
        } e[ME];
        int le,head[MV];
        void init() {
            le=0;
            mt(head,-1);
        }
        void add(int u,int v,typef flow) {
            e[le].u=u;
            e[le].v=v;
            e[le].flow=flow;
            e[le].next=head[u];
            head[u]=le++;
        }
    } g;
    typef getflow() {
        return flow;
    }
    void init() {
        g.init();
    }
    void add(int u,int v,typef flow) {
        g.add(u,v,flow);
        g.add(v,u,0);
    }
    void solve(int s,int t) {
        int p,tempp;
        typef now;
        bool flag;
        flow=0;
        while(bfs(s,t)) {
            for(int i=0; i<MV; i++) {
                temp[i]=g.head[i];
                used[i]=true;
            }
            p=1;
            path[p]=s;
            while(p) {
                int u=path[p];
                if(u==t) {
                    now=inf;
                    for(int i=1; i<p; i++) {
                        now=min(now,g.e[cur[path[i]]].flow);
                    }
                    flow+=now;
                    for(int i=1; i<p; i++) {
                        int j=cur[path[i]];
                        g.e[j].flow-=now;
                        g.e[j^1].flow+=now;
                        if(!g.e[j].flow) tempp=i;
                    }
                    p=tempp;
                } else {
                    flag=false;
                    for(int i=temp[u]; ~i; i=g.e[i].next) {
                        int v=g.e[i].v;
                        if(used[v]&&g.e[i].flow&&level[u]+1==level[v]) {
                            cur[u]=i;
                            temp[u]=g.e[i].next;
                            flag=true;
                            path[++p]=v;
                            break;
                        }
                    }
                    if(flag) continue;
                    p--;
                    used[u]=false;
                }
            }
        }
    }
} dinic;

bool vis[5010];
void dfs(int u,int t){
    if(u == t) return;
    vis[u] = true;
    for(int i=dinic.g.head[u];~i;i=dinic.g.e[i].next){
        int v = dinic.g.e[i].v;
        if(dinic.g.e[i].flow && !vis[v]){
            dfs(v,t);
        }
    }
}

int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m)) {
        dinic.init();
        ///S为源点,T为汇点
        ///S = 0,T = n+1
        int S = 0;
        int T = n+1;
        LL sum = 0;
        for(int i=1; i<=n; i++) {
            scanf("%lld",&value[i]);
            if(value[i] > 0) {
                dinic.add(S,i, value[i]);
                sum += value[i];
            } else {
                dinic.add(i,T,-value[i]);
            }
        }
        int u,v;
        for(int i=0; i<m; i++) {
            scanf("%d%d",&u,&v);
            dinic.add(u,v,inf);
        }

        dinic.solve(S,T);
        LL profit = dinic.getflow();

        mt(vis,false);
        dfs(S,T);
        int person = 0;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]) person++;
        }

        printf("%d %lld\n",person,sum - profit);

    }


    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值