poj Firing(最大权闭合图)

Firing

题目:

要解雇一些人,而解雇的这些人如果人跟他有上下级的关系,则跟他有关系的人也要一起解雇。每个人都会创造一定的价值,要求你求出在最大的获利下,解雇的人最小。

算法分析:

在这之前要知道一个定理:

最小割 = 最大流

一道最大权闭合图的裸题,而这又可以转换成最小割来求解。证明可以看2007年胡伯涛的论文则可以直接套出模板,没看过的最好去看一下,那里解释的清楚。这里我给出他的原文的一些构造方法。

增加源s汇t

源s连接原图的正权点,容量为相应点权

原图的负权点连接汇t,容量为相应点权的相反数

原图边的容量为正无限.

而这里其实最难的是第一问。而由于本人的实力有限。所以,难以解释清楚。但是网上流传的该题解题报告的人很少有解释清的,都是一笔带过。找了好久才找到了一篇正确的解释。下面给出解释。

////////////////////////////////////////////////////////////////

标准的最大权闭合图,构图:从源点s向每个正收益点连边,容量为收益;从每个负收益点向汇点t连边,容量为收益的相反数;对于i是j的上司,连边i->j,容量为inf。最大收益 = 正收益点权和 - 最小割 = 正收益点权和 - 最大流(胡波涛论文上有证明)。这题的关键是如何在最小割的前提下求出最少的割边数目,可以从源点对残量网络进行一次DFS,每个割都会将源汇隔开,所以从源点DFS下去一定会因为碰到某个割而无法前进,用反证法易知这时遍历过的点数就是S集的最少点数。

/////////////////////////////////////////////////////////////////

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;
const int MAXN = 5000 + 10;
const LL INF = (1LL) << 60;       //必须(1LL)!!!!!!!!!!!!!!!!!!!!  T_T......
struct Edge{
   int from,to;
   LL cap,flow;
   Edge(){};
   Edge(int _f,int _t,LL _c,LL _fw)
        :from(_f),to(_t),cap(_c),flow(_fw){};
};

vector<Edge> edges;
vector<int> G[MAXN];
bool vst[MAXN];
int d[MAXN],cur[MAXN];
int V,E,S,T;
int cnt;         //最少割边数
LL ans,sum;

void clr(){
    ans = sum = 0;
    S = 0; T = V + 1;
    for(int i = 0;i <= V;++i)
        G[i].clear();
    edges.clear();
}

void addEdge(int f,int t,LL cap){
    edges.push_back(Edge(f,t,cap,0));
    edges.push_back(Edge(t,f,0,0));
    int sz = edges.size();
    G[f].push_back(sz - 2);
    G[t].push_back(sz - 1);
}

void init(){
    LL x;
    for(int i = 1;i <= V;++i){
        scanf("%I64d",&x);
        if(x > 0){
            addEdge(S,i,x);
            sum += x;
        } else {
           addEdge(i,T,-x);
        }
    }

    int a,b;
    for(int i = 0;i < E;++i){
        scanf("%d%d",&a,&b);
        addEdge(a,b,INF);
    }
}

bool BFS(){
   memset(vst,0,sizeof(vst));
   queue<int> Q;
   Q.push(S);
   d[S] = 0;
   vst[S] = 1;

   while(!Q.empty()){
      int x = Q.front();  Q.pop();
      for(int i = 0;i < (int)G[x].size();++i){
          Edge& e = edges[G[x][i]];
          if(!vst[e.to] && e.cap > e.flow){
              vst[e.to] = 1;
              d[e.to] = d[x] + 1;
              Q.push(e.to);
          }
      }
   }

   return vst[T];
}

LL DFS(int x,LL a){
   if(x == T||a == 0)
      return a;

   LL flow = 0,f;
   for(int& i = cur[x];i < (int)G[x].size();++i){
       Edge& e = edges[G[x][i]];
       if(d[x] + 1 == d[e.to]&&(f = DFS(e.to,min(a,e.cap - e.flow))) > 0){
           e.flow += f;
           edges[G[x][i]^1].flow -= f;
           flow += f;
           a -= f;
           if(a == 0) break;
       }
   }
   return flow;
}

LL Maxflow(){
   LL flow = 0;
   while(BFS()){
       memset(cur,0,sizeof(cur));
       flow += DFS(S,INF);
   }

   return flow;
}

//求解在最小割的前提下,得最好割边
void dfs(int u){
   vst[u] = 1;
   for(int i = 0;i < (int)G[u].size();++i){
      Edge& e = edges[G[u][i]];
      if(!vst[e.to] && e.cap > e.flow){
         cnt++;
         dfs(e.to);
      }
   }
}

void solve(){

    LL ans = sum - Maxflow();

    cnt = 0;
    memset(vst,0,sizeof(vst));
    dfs(S);

    printf("%d %I64d\n",cnt,ans);
}

int main()
{
   // freopen("Input.txt","r",stdin);

    while(~scanf("%d%d",&V,&E)){
        clr();
        init();

        solve();
    }
    return 0;
}
时间: 2024-10-07 11:58:45

poj Firing(最大权闭合图)的相关文章

POJ 2987 Firing (最大权闭合图,最小割)

http://poj.org/problem?id=2987 Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7865   Accepted: 2377 Description You've finally got mad at "the world's most stupid" employees of yours and decided to do some firings. You're n

POJ 2987 Firing 最大权闭合图

点击打开链接 Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7976   Accepted: 2409 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 giv

poj 2987 最大权闭合图

Language: Default Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 8744   Accepted: 2631 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

POJ2987 Firing 最大权闭合图

详情请参考http://www.cnblogs.com/kane0526/archive/2013/04/05/3001557.html 值得注意的地方,割边会把图分成两部分,一部分和起点相连,另一部分和汇点相连 我们只需要关注和起点相连的点的点就好,如何统计呢? 只需要从起点开始搜索,只要边不是满流,一直搜就好 然后,答案就是总权值-最小割 注:对于dinic网络流,我还是喜欢LRJ白书上的,用起来方便 #include <cstdio> #include <cstdlib> #

poj - 2987 - Firing(最大权闭合图)

题意:n(0 < n ≤ 5000)个人,m(0 ≤ m ≤ 60000)个上下级关系,炒一个人可以获得收益或者损失bi (|bi| ≤ 10 ^ 7, 1 ≤ i ≤ n),炒一个人会把他的所有下级一起炒掉,问怎样炒人使收益最大,输出最大收益和最少炒人的数量. 题目链接:http://poj.org/problem?id=2987 -->>炒一个人会把他的所有下级一起炒掉,这时存在依赖关系,对应图论中的闭合图..最大收益对应最大权和..于是,最大权闭合图上场.. 最少炒人数?获得最大收

POJ 2987 Firing 网络流 最大权闭合图

http://poj.org/problem?id=2987 https://blog.csdn.net/u014686462/article/details/48533253 给一个闭合图,要求输出其最大权闭合图的权值和需要选的最少点数,最大权闭合图定义和网络流连边方式见博客. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include&

POJ_2987_Firing(最大流+最大权闭合图)

描述 http://poj.org/problem?id=2987 要炒员工鱿鱼,炒了一个人,他的下属一定被炒.给出每个人被炒后公司的收益(负值表示亏损),问怎样炒公司收益最大,以及这种方法炒了几个人.(先输出人数) 分析 求最大收益是用最大权闭合图.要炒一个人,他的下属也要被炒,那么边由上司连向下属,求最大权闭合图即可. 求炒了几个人实际是就是求先前求出来的那个最大权闭合图中点的个数.最大权闭合图对应的是最小割,而最小割中的边都是满流的,所以从源点出发无法到达闭合图的补集(即T),并且由于是闭

HDU 3061:Battle(最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=3061 题意:中文题意. 思路:和上一题神似啊,比上一题还简单,重新看了遍论文让我对这个理解更加深了. 闭合图:如果某个点在图中的话,那么这个点的后继点全部都要在图中. 对应至题目,这里的必须攻占b以后才能攻占a,那么是a依赖于b.如果a在图中的话,那么b必定在图中(因为a是依赖于b的),所以是a连向b(而不是b连向a). 这里总结一下做最大权闭合图的套路:把权值为正的点与超级源点S相连,容量为该权值,把权值为

HDU5772 String problem 最大权闭合图+巧妙建图

题意:自己看吧(不是很好说) 分析: 网络流:最大权闭合子图. 思路如下: 首先将点分为3类 第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分) 第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费) 第三类:对于10种字符拆出10个点,每个点的权值为  -(b[x]-a[x]) 那么我们可以得到一个关系图 ,对于第一类中的点Pij,如果想要选择Pij,你就必须要选中第二类中的点i和j,对于第二类中的点