poj 2987

题意:一个公司要解雇一部分人。如果解雇一个人,那么这个人的下属也要被解雇。解雇一个人可能有损失,也可能会有收益,因为员工的价值量不一样嘛。为最少解雇多少个人可使公司获得最大的利益。

思路:最大权闭合图与最小割+最大流模板。以下为网上找到的,特此声明:

怎样求割:求完最大流后,在 残留网络中从source开始dfs,被染色的为S,未被染色的为T,则边集[S,T]为割。 (或者从sink反向dfs,被染色的为T,未被染色的为S,边集[S,T]为割。另外可参考这个网址:http://blog.csdn.net/waitfor_/article/details/7330437
 
#include <stdio.h>
#include <string.h>
#define VM 10000
#define EM 120000
#define inf 0x3f3f3f3f
struct E
{
    int to,cap,nxt;
}edge[EM];

int head[VM],gap[VM],dist[VM],cur[VM],pre[VM];
int ep;
void addedge (int cu,int cv,int cw)
{

    edge[ep].to = cv;
    edge[ep].cap = cw;
    edge[ep].nxt = head[cu];
    head[cu] = ep;
    ep ++;
    edge[ep].to = cu;
    edge[ep].cap = 0;
    edge[ep].nxt = head[cv];
    head[cv] = ep;
    ep ++;
}
int min (int a ,int b)
{
   return a > b ? b : a;
}

long long int sap (int src,int des,int n)
{

    memset (dist,0,sizeof(dist));
    memset (gap,0,sizeof (dist));
    memcpy (cur,head,sizeof(dist));
    long long int res = 0;
    int u = pre[src] = src;
    int aug = inf;
    gap[0] = n;
    while (dist[src] < n)
    {
loop:
        for (int &i = cur[u];i != -1;i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (edge[i].cap && dist[u] == dist[v] + 1)
            {
                aug = min (aug,edge[i].cap);
                pre[v] = u;
                u = v;
                if (v == des)
                {
                    res += aug;
                    for (u = pre[u];v != src;v = u,u = pre[u])
                    {
                        edge[cur[u]].cap -= aug;
                        edge[cur[u]^1].cap += aug;
                    }
                    aug = inf; //
                }
                goto loop;
            }
        }
        int mindist = n;  //
        for (int i = head[u];i != -1;i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (edge[i].cap && mindist > dist[v])
            {
                cur[u] = i;
                mindist = dist[v];
            }
        }
        if ((--gap[dist[u]]) == 0)
            break;
        dist[u] = mindist + 1;
        gap[dist[u]] ++;
        u = pre[u];
    }
    return res;
}
int vis[VM];
void dfs(int u,int v)
{
    if(u==v) return ;
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    if(edge[i].cap>0&&!vis[edge[i].to])
    dfs(edge[i].to,v);
}
int main ()
{
    int n,m,u,v,p;
    int src,des;
    long long int sum;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        ep = 0;
        sum=0;
        src = 0;
        des = n + 1;
        memset (head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p);
            if(p>0)
            {
                addedge(src,i,p);
                sum+=p;
            }
            else
            addedge(i,des,-p);
        }
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v,inf);
        }
        long long int max_flow=sap(src,des,n + 2);;
        memset(vis,0,sizeof(vis));

        dfs(src,des);
        int ans=0;
        for(int i=1;i<=n;i++)
        if(vis[i])
        ans++;
        printf("%d %lld\n",ans,sum-max_flow);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值