poj 2987(最大权闭合图)

题目链接:http://poj.org/problem?id=2987

题意:某公司想要裁员,裁员的标准是如果某人被裁,那么其下属也会被裁,依此类推,每一个人都有一个贡献度,问怎样裁员才能使得最后的贡献度最大并且裁掉人数最少?

 

以下内容参考自胡伯涛 《最小割模型在信息学竞赛中的应用》以及 http://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html

闭合图:在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。

最大权闭合图:在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。

上图中闭合图有

     {5}、{2,5}、{4,5}

     {2,4,5}、{3,4,5}

     {1,2,3,4,5}、{1,2,4,5}

最大权闭合图为{3,4,5}。权值和为4.

怎样求解最大权闭合图呢??

构图:我们设立一个超级源点和一个超级汇点,从超级源点向所有权值为正的点连一条单向边,边的权值为点的权值;从所有权值为负的点向超级汇点连一条单向边,边的权值为点的权值的绝对值;然后图中所有的边都设为无穷大。得到如下图:

然后求出此题的最小割,用所有权值为正的点之和减掉最小割即为最大权闭合图。

以下是证明过程,引自http://www.cnblogs.com/wuyiqi/archive/2012/03/12/2391960.html

首先引入结论,最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图,接下来我们来说明一些结论。

  • 证明:最小割为简单割。

        引入一下简单割的概念:割集的每条边都与S或T关联。(请下面阅读时一定分清最小割与简单割,容易混淆)

        那么为什么最小割是简单割呢?因为除S和T之外的点间的边的容量是正无穷,最小割的容量不可能为正无穷。所以,得证。

  • 证明网络中的简单割与原图中闭合图存在一一对应的关系。(即所有闭合图都是简单割,简单割也必定是一个闭合图)。

        证明闭合图是简单割:如果闭合图不是简单割(反证法)。那么说明有一条边是容量为正无穷的边,则说明闭合图中有一条出边的终点不在闭合图中,矛盾。

        证明简单割是闭合图:因为简单割不含正无穷的边,所以不含有连向另一个集合(除T)的点,所以其出边的终点都在简单割中,满足闭合图定义。得正。

  • 证明最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图。

        首先我们记一个简单割的容量为C,且S所在集合为N,T所在集合为M。

        则C=M中所有权值为正的点的权值(即S与M中点相连的边的容量)+N中所有权值为负的点权值的绝对值(即N中点与T中点相连边的容量)。记(C=x1+y1);(很好理解,不理解画一个图或想象一下就明白了)。

        我们记N这个闭合图的权值和为W。

        则W=N中权值为正的点的权值-N中权值为负的点的权值的绝对值。记(W=x2-y2);

        则W+C=x1+y1+x2-y2。

        因为明显y1=y2,所以W+C=x1+x2;

        x1为M中所有权值为正的点的权值,x2为N中权值为正的点的权值。

        所以x1+x2=所有权值为正的点的权值之和(记为TOT).

        所以我们得到W+C=TOT.整理一下W=TOT-C.

        到这里我们就得到了闭合图的权值与简单割的容量的关系。

        因为TOT为定值,所以我们欲使W最大,即C最小,即此时这个简单割为最小割,此时闭合图为其源点S所在集合(除去S)。得正。

 

至此,我们就将最大权闭合图问题转化为了求最小割的问题。求最小割用最小割容量=最大流,即可将问题转化为求最大流的问题。

 

最后的结论为:最大权闭合图 :原边改为正无穷后,添加S和T,S向所有点连正权,所有点向T连负权的绝对值.求最小割,用所有权值为正的点相加之和减掉最小割得到最大权闭合图。

———————————————————————————————————————————————————————————————————————题目分割线

poj 2987:上面的证明过程已经说了:最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图。所以这题的最少人数在残余网络中做一次深搜,得到的点即为最少裁员的人数,最大贡献值即为最大权闭合图。而这题的数据量达到了5000*10^7,所以要用_int64保存结果.

#include <stdio.h>
#include <algorithm>
#include <queue>
#include <string.h>
#include <math.h>
#include <iostream>
#include <math.h>
using namespace std;
typedef long long LL;
const int N = 5005;
const int INF = 999999999;
struct Edge{
    int v,next;
    int w;
}edge[N*N];
int head[N];
int level[N];
int tot;
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void addEdge(int u,int v,int w,int &k){
    edge[k].v = v,edge[k].w=w,edge[k].next=head[u],head[u]=k++;
    edge[k].v = u,edge[k].w=0,edge[k].next=head[v],head[v]=k++;
}
int BFS(int src,int des){
    queue<int >q;
    memset(level,0,sizeof(level));
    level[src]=1;
    q.push(src);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        if(u==des) return 1;
        for(int k = head[u];k!=-1;k=edge[k].next){
            int v = edge[k].v;
            int w = edge[k].w;
            if(level[v]==0&&w!=0){
                level[v]=level[u]+1;
                q.push(v);
            }
        }
    }
    return -1;
}
int dfs(int u,int des,int increaseRoad){
    if(u==des) return increaseRoad;
    int ret=0;
    for(int k=head[u];k!=-1;k=edge[k].next){
        int v = edge[k].v;
        int w = edge[k].w;
        if(level[v]==level[u]+1&&w!=0){
            int MIN = min(increaseRoad-ret,w);
            w = dfs(v,des,MIN);
            edge[k].w -=w;
            edge[k^1].w+=w;
            ret+=w;
            if(ret==increaseRoad) return ret;
        }
    }
    return ret;
}
LL Dinic(int src,int des){
    LL ans = 0;
    while(BFS(src,des)!=-1) ans+=(LL)dfs(src,des,INF*1.0);
    return ans;
}
int ans=0;
bool vis[N];
void dfs(int u){
    ans++;
    vis[u] = true;
    for(int k = head[u];k!=-1;k=edge[k].next){
        if(edge[k].w>0&&!vis[edge[k].v]){
            dfs(edge[k].v);
        }
    }
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        int src = 0,des = n+1;
        LL sum=0;
        for(int i=1;i<=n;i++){
            int w;
            scanf("%d",&w);
            if(w<0) addEdge(i,des,-w,tot);
            else addEdge(src,i,w,tot),sum+=w;
        }
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            addEdge(u,v,INF,tot);
        }
        LL MAX = sum-Dinic(src,des);
        memset(vis,false,sizeof(vis));
        dfs(src);
        ans-=1; ///除去超级源点
        printf("%d %I64d\n",ans,MAX);
    }
}

 

转载于:https://www.cnblogs.com/liyinggang/p/5553665.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值