POJ 2987 Firing 最大权闭合图

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 individually bi (|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.

题意:

裁员吧,给出每个员工的价值(有的人是负的),给出员工的直属关系,裁掉领导就必须裁下属,可能成环,问怎样裁员才能得到最大利益,如果有多解,输出裁员人数最少的那一个。

解题思路:

求最大闭合权图,(闭合图就是该图中每个点都不能连到此图外面去)。

方法:把价值为证的员工连接源点,价值为负的员工连接汇点,然后根据直属关系连接两种员工,在该图上跑一遍最大流(我用的Dinic),然后以求出的最小割为界,连接原点的那一部分就是保留下来的员工。

最小割大概就是:水流从原点出发,把最先遇到的一系列满流路径,用一条线贯穿,割线左边的集合跟源点联通,右边的集合跟汇点联通。吧?

代码:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=5000+10;
const int MAXM=60000*2+5000*2;
int n,m;
int s,t,e_max,fir[MAXN],dis[MAXN];
int val[MAXN];
int vis[MAXN];
int number;
long long profit;
int q[999999];
struct
{
    int u,v,rflow,nex;   //rflow是残余流量
}edge[MAXM];
void add_edge(int u,int v,int rflow)
{
    edge[e_max].u=u;edge[e_max].v=v;edge[e_max].rflow=rflow;edge[e_max].nex=fir[u];
    fir[u]=e_max++;
    edge[e_max].u=v;edge[e_max].v=u;edge[e_max].rflow=0;edge[e_max].nex=fir[v];
    fir[v]=e_max++;
}
void init()
{
        memset(fir,-1,sizeof(fir));
        e_max=0;
}
int bfs()
{
    int i,x,v,r=1,f=0;
    memset(dis,0,sizeof(dis));
    dis[s]=1;
    q[0]=s;
    while(f<r)
    {
        x=q[f++];
        for(i=fir[x];i!=-1;i=edge[i].nex)
            if(edge[i].rflow&&dis[v=edge[i].v]==0)
            {
                dis[v]=dis[x]+1;
                if(v==t)
                    return 1;
                q[r++]=v;
            }
    }
    return 0;
}
int dfs(int s,int limit)
{
        if(s==t)
                return limit;
        int i,v,tmp,cost=0;
        for(i=fir[s];i!=-1;i=edge[i].nex)
        if(edge[i].rflow&&dis[s]==dis[v=edge[i].v]-1)
        {
            tmp=dfs(v,min(limit-cost,edge[i].rflow));
            if(tmp>0)
            {
                edge[i].rflow-=tmp;             //修改残余网络
                edge[i^1].rflow+=tmp;
                cost+=tmp;
                if(limit==cost)
                    break;
            }
            else dis[v]=-1;                   //标记为-1以后不再访问
        }
    return cost;
}
int Dinic()
{
    s=0,t=n+1;
    int ans=0;
    while(bfs())                              //bfs(分层)
        ans+=dfs(s,INF);                      //dfs()按层级寻找增广路,起点流量无限制
    return ans;
}
void RAM()
{
        init();
        for(int i=1;i<=n;i++)
        {
                cin>>val[i];
                if(val[i]>=0)
                        add_edge(0,i,val[i]);
                else
                        add_edge(i,n+1,(-1)*val[i]);
        }
        for(int i=1;i<=m;i++)
        {
                int a,b;
                cin>>a>>b;
                add_edge(a,b,INF);
        }
}
void bfs2()
{
        memset(vis,0,sizeof vis);
        number=0;
        profit=0;
        int f=0,r=1;
        q[0]=s;
        vis[0]=1;
        while(f<r)
        {
                int now=q[f++];
                for(int i=fir[now];~i;i=edge[i].nex)
                {
                        if(edge[i].rflow!=0&&!vis[edge[i].v])
                        {
                                vis[edge[i].v]=1;
                                number++;
                                profit+=val[edge[i].v];
                                q[r++]=edge[i].v;
                        }
                }
        }
}
int main()
{
        while(cin>>n>>m)
        {
                RAM();
                Dinic();
                bfs2();
                cout<<number<<' '<<profit<<endl;
        }
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值