• POJ 2987 Firing【最大权闭合图-最小割】


    题意:给出一个有向图,选择一个点,则要选择它的可以到达的所有节点。选择每个点有各自的利益或损失。求最大化的利益,以及此时选择人数的最小值。

    算法:构造源点s汇点t,从s到每个正数点建边,容量为利益。每个负点到t建边,容量为损失的绝对值。其他关系边容量正向无穷,反向0。正数点总和减去最小割即为最大权闭合图答案。因为残余网络不会对0流边处理,所以不会将0流点选入取点集,所以最小割的取法中为被选中的点。

    最大权闭合图的求解方法:

    1. 先构造网络流N,添加源点s,从s到正权值点做一条边,容量为点的权值。

    2. 添加汇点t,从负权值点到t做一条边,容量为点的权值的绝对值。

    3. 原来的边的容量统统设为无穷大。比如:

      转换为

    4. 求解最小割:最大权=正权值之和-最小割权值

    5. 残余网络中的点的个数即为裁员个数。

    思路:最大权闭合图。用Dinic求最小割

    要得到最大收益,就是尽量选择更多收益为正数的人,选择更少收益为负数的人,因此我们将收益为正数的人与源点连一条边,将收益为负数的人与汇点连一条边,这样得到的割集就是未选择的收益为正数的人+选择的收益为负数的人(也可以是损失的收益),要使这个割集越小越好,那么就是求最小割。最大收益是所有正数的和sum,再用sum-最小割(损失的收益)就可以得到最大收益。

    最少的裁员人数,最小割后的的残余网络中,只要能从S遍历到的点,就可以看成是被裁掉的点,因为最终肯定会遇到割边达不到T,所以我们直接DFS残余网络的点数就可以得到最少的裁员人数了。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <queue>
      5 #include <vector>
      6 using namespace std;
      7 typedef long long LL;
      8 
      9 const LL INF = 0x3f3f3f3f3f3f3f3f;
     10 
     11 struct edge
     12 {
     13     edge(int _v,int _r, LL _c):v(_v),r(_r),c(_c){};
     14     int v,r;
     15     LL c;
     16 };
     17 
     18 vector<edge> e[5010];
     19 
     20 void add_edge(int u,int v,LL c)
     21 {
     22     e[u].push_back(edge(v,e[v].size(),c));
     23     e[v].push_back(edge(u,e[u].size()-1,0));
     24 }
     25 
     26 int level[5010];
     27 int iter[5010];        // 当前弧,在其之前的边已经没有用了
     28 queue<int> q;
     29 
     30 bool bfs(int s,int t)
     31 {
     32     memset(level,0, sizeof(level));
     33     while (!q.empty()) q.pop();
     34     q.push(s);
     35     level[s]=1;
     36     while(!q.empty())
     37     {
     38         int u=q.front();q.pop();
     39         for(int i=0;i<e[u].size();i++)
     40         {
     41             int v=e[u][i].v;
     42             if(e[u][i].c>0&&!level[v])
     43             {
     44                 q.push(v);
     45                 level[v]=level[u]+1;
     46             }
     47             if(level[t])return 1;
     48         }
     49     }
     50     return 0;
     51 }
     52 
     53 LL dfs(int s,int t, LL flow)
     54 {
     55     if(s==t)return flow;
     56     for(int& i = iter[s];i<e[s].size();i++) // 一次增广返回时,记录当前弧
     57     {
     58         int v = e[s][i].v;
     59         if(e[s][i].c>0&&level[v]==level[s]+1)
     60         {
     61             LL k = dfs(v,t,min(flow,e[s][i].c));
     62             if(k>0)
     63             {
     64                 e[s][i].c-=k;
     65                 e[v][e[s][i].r].c+=k;
     66                 return k;
     67             }
     68         }
     69     }
     70     return 0;
     71 }
     72 
     73 LL Dinic(int s,int t)
     74 {
     75     LL flow=0;
     76     while(bfs(s,t))
     77     {
     78         LL f=0;
     79         memset(iter, 0, sizeof(iter));
     80         while((f=dfs(s,t,INF))>0)flow+=f;
     81     }
     82     return flow;
     83 }
     84 
     85 bool vis[5010];
     86 int cnt;
     87 void GaoCnt(int u)//dfs统计残余图的正边
     88 {
     89     ++cnt;
     90     vis[u]=1;
     91     for(int i=0;i<e[u].size();i++)
     92     {
     93         int v=e[u][i].v;
     94         if(e[u][i].c>0&&!vis[v])
     95             GaoCnt(v);
     96     }
     97 }
     98 
     99 
    100 int main()
    101 {
    102     int n,m;
    103     scanf("%d%d",&n,&m);
    104     int s=0,t=n+1;// 源点,汇点
    105     LL ans=0;
    106     for(int i=1;i<=n;i++)
    107     {
    108         LL c;
    109         scanf("%lld",&c);
    110         if(c>0)
    111         {
    112             ans+=c; //正权值求和
    113             add_edge(s,i,c);
    114         }
    115         else if(c<0)
    116         {
    117             add_edge(i,t,-c);
    118         }
    119     }
    120     for (int i = 0; i < m; ++i) {
    121         int u,v;
    122         scanf("%d%d",&u,&v);
    123         add_edge(u,v,INF);
    124     }
    125     ans-= Dinic(s,t);
    126     GaoCnt(s);
    127     printf("%d %lld
    ",cnt-1,ans);
    128     return 0;
    129 }

    参考自:hankcs

  • 相关阅读:
    NEC学习 ---- 模块 -多行式面包屑导航
    NEC学习 ---- 模块 -文本圆角背景导航
    NEC学习 ---- 布局 -三列,右侧自适应
    NEC学习 ---- 布局 -三列,左侧自适应
    NEC学习 ---- 布局 -三列, 左右定宽,中间自适应
    NEC学习 ---- 布局 -两列定宽
    NEC学习 ---- 布局 -两列, 右侧定宽,左侧自适应
    jquery weibo 留言
    原生js+本地储存登录注册
    原生捕鱼
  • 原文地址:https://www.cnblogs.com/demian/p/9234825.html
Copyright © 2020-2023  润新知