zoukankan      html  css  js  c++  java
  • 【POJ 2987】Firing (最小割-最大权闭合子图)

    裁员
    【问题描述】 
    在一个公司里,老板发现,手下的员工很多都不务正业,真正干事员工的没几个,于是老板决定大裁员,每开除一个人,同时要将其下属一并开除,如果该下属还有下属,照斩不误。给出每个人的贡献值和从属关系,求在最大贡献值的前提下最小剩下多少人及最大贡献值。留下多少人无所谓,现在老板想知道留下的人最大的贡献值是多少。
    【输入描述】
        第一行两个整数n,m,表示有多少个员工与多少个从属关系。
    第二行n个整数,表示每个员工的贡献值。
    接着m行,每行两个数x,y,表示x是y的下属,一个员工可能有多个下属但不会有多个上司
    【输出描述】
    包括两个数,表示最大贡献值前提下最小剩下多少人及最大贡献值和。
    【其他说明】:
    0 < n ≤ 5000
    0 ≤ m ≤ 60000
    员工价值≤107
    样例中留下4,5号员工是最好情况

    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.

    Output

    Output two integers separated by a single space: the minimum number of employees to fire to achieve the maximum profit, and the maximum profit.

    Sample Input

    5 5
    8
    -9
    -20
    12
    -10
    1 2
    2 5
    1 4
    3 4
    4 5

    Sample Output

    2 2
    
    

    【分析】

      最大权闭合子图。

      假设每个正权的人都留着,然后求最少要减掉多少(即还裁掉多少正权的人,雇佣多少负权的人才能满足情况)

      所以 对于w[i]>0的i add(st,i,w[i])

         对于w[i]|<0 的i add(i,ed,-w[i])

      原图 x->y add(s,y,INF)

      然后求最小割。

      但是这题要最大流的情况下,点数最小。这个 不是 很懂 明天再说吧= =

      我看别人是dfs的,然后我也dfs了。

      还有什么厉害的放大边权的方法(其实之前已经领教过一次了)ORZ。。。

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 #define Maxn 5010
      9 #define Maxm 60010
     10 #define INF 0xfffffff
     11 #define LL long long
     12 
     13 struct node
     14 {
     15     LL x,y,f,o,next;
     16 }t[Maxm*2];LL len;
     17 LL first[Maxn];
     18 
     19 void ins(LL x,LL y,LL f)
     20 {
     21     t[++len].x=x;t[len].y=y;t[len].f=f;
     22     t[len].next=first[x];first[x]=len;t[len].o=len+1;
     23     t[++len].x=y;t[len].y=x;t[len].f=0;
     24     t[len].next=first[y];first[y]=len;t[len].o=len-1;
     25 }
     26 
     27 LL mymin(LL x,LL y) {return x<y?x:y;}
     28 
     29 LL w[Maxn],st,ed;
     30 LL dis[Maxn];
     31 
     32 queue<LL > q;
     33 bool bfs()
     34 {
     35     while(!q.empty()) q.pop();
     36     memset(dis,-1,sizeof(dis));
     37     q.push(st);dis[st]=0;
     38     while(!q.empty())
     39     {
     40         LL x=q.front();
     41         for(LL i=first[x];i;i=t[i].next) if(t[i].f>0)
     42         {
     43             LL y=t[i].y;
     44             if(dis[y]==-1)
     45             {
     46                 dis[y]=dis[x]+1;
     47                 q.push(y);
     48             }
     49         }
     50         q.pop();
     51     }
     52     if(dis[ed]==-1) return 0;
     53     return 1;
     54 }
     55 
     56 LL ffind(LL x,LL flow)
     57 {
     58     if(x==ed) return flow;
     59     LL now=0;
     60     for(LL i=first[x];i;i=t[i].next) if(t[i].f>0)
     61     {
     62         LL y=t[i].y;
     63         if(dis[y]==dis[x]+1)
     64         {
     65             LL a=ffind(y,mymin(flow-now,t[i].f));
     66             t[i].f-=a;
     67             t[t[i].o].f+=a;
     68             now+=a;
     69         }
     70         if(now==flow) break;
     71     }
     72     if(now==0) dis[x]=-1;
     73     return now;
     74 }
     75 
     76 LL max_flow()
     77 {
     78     LL ans=0;
     79     while(bfs())
     80     {
     81         ans+=ffind(st,INF);
     82     }
     83     return ans;
     84 }
     85 
     86 bool vis[Maxn];
     87 void dfs(LL x)
     88 {
     89     vis[x]=1;
     90     for(LL i=first[x];i;i=t[i].next) if(!vis[t[i].y]&&t[i].f>0)
     91         dfs(t[i].y);
     92 }
     93 
     94 int main()
     95 {
     96     LL n,m;
     97     LL ans=0,sum=0;
     98     scanf("%lld%lld",&n,&m);
     99     for(LL i=1;i<=n;i++) {scanf("%lld",&w[i]);if(w[i]>0) ans+=w[i];}
    100     st=n+1,ed=st+1;
    101     for(LL i=1;i<=m;i++)
    102     {
    103         LL x,y;
    104         scanf("%lld%lld",&x,&y);
    105         ins(x,y,INF);
    106     }
    107     for(LL i=1;i<=n;i++) if(w[i]>0) ins(st,i,w[i]);
    108     for(LL i=1;i<=n;i++) if(w[i]<0) ins(i,ed,-w[i]);
    109     LL fl=max_flow();
    110     ans-=fl;
    111     memset(vis,0,sizeof(vis));
    112     dfs(st);
    113     for(LL i=1;i<=n;i++) if(vis[i]) sum++;
    114     printf("%lld %lld
    ",sum,ans);
    115     return 0; 
    116 }
    View Code

    放点不是我写的,比我可信的 最大权闭合子图 解释

    1、本题某些东西的解释

    本题还有一个要求就是要求不仅要是最大权,并且要求点数还最少
    看一个神牛的证明----http://hi.baidu.com/dispossessed/blog/item/2396c0ddbc73a2caa044df44.html

    下面证明最小割对应取点方案就是最小取点数
    由于原图是个DAG图,所以对于取得的最大权闭合图K,取它的任意一个子图G,如果从K-G仍然是一个闭合图,那么的点权和一定大于等于0,例如:1->2,2->3,1->4,4->5,若最大权闭合图为:{1,2,3,4,5},那么其中任一满足条件的G({1},{1,2},{1,2,3},{1,4},{1,4,5})点权和一定大于等于0,否则去除G,K-G仍然为闭合图,但是K-G的点权和会大于K
    所以如果有两种取点方式使得权值相同,但是取点数不同的话,那么肯定存在一个可以移除的满足条件的子图G,其点权和为0
    下面考虑构造的网络,对于G在网络中的对应图G',由于在网络求的是最小割,即最大流,而且G的点权和为0,所以G'中与源点S连边的容量和等于G'中与汇点T连边的流量和,同时由于去除G后K还是一个闭合图,所以只有可能G'中的流量流入K'-G',不可能有流量从K'-G'流入G',所以G'的边中除了流量为inf的那些,一定是满流的
    再考虑在残留网络中求出取点集的方法,从源点开始floodfill,忽略满流边,即残留网络中的0流边,可以遍历到的点就是要取的点集了,这个道理想一下简单割和闭合图的取法一一对应就可以了
    那么G'既然是满流的,在残留网络中就不可能对这些0流边进行处理,那就不会取到G中的点进入取点集,所以建立网络求得得最小割对应的取法取出的就是最小的点数了
    --------------------------------

    当然还有一种是神奇的放大边权方法

    建图前,对所有b[i],执行变换b[i]=b[i]*10000-1,然后,会惊异地发现,
    此时最大流所对应的方案就是满足辞退最少人数的了。
    为什么?显然,变换后的流量r2除以10000后再取整就等于原来的流量,但是
    r2的后四位却蕴含了辞退人数的信息:每多辞退一个人,流量就会少1。

    转自:http://blog.csdn.net/sdj222555/article/details/7797534

    2、最大权闭合子图

    3、ORZ 胡伯涛

    太长了不放了,自己看吧。。

    2016-11-03 22:00:24

    好困Zzz...

  • 相关阅读:
    端口映射到公网工具
    C# app.config文件配置和修改
    C#基本知识点-Readonly和Const的区别
    C#知识点-StopWatch-计时
    python with open as f写中文乱码
    程序员不要去这样的公司
    关于老系统的重构和优化选择
    .Net Remoting 调用远程对象
    自定义的Config节点及使用
    前台线程和后台线程区别与使用技巧
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6028392.html
Copyright © 2011-2022 走看看