zoukankan      html  css  js  c++  java
  • UPC-5120 Open-Pit Mining(最大权闭合子图)

    题目描述
    Open-pit mining is a surface mining technique of extracting rock or minerals from the earth by their removal from an open pit or borrow. Open-pit mines are used when deposits of commercially useful minerals or rocks are found near the surface. Automatic Computer Mining (ACM) is a company that would like to maximize
    its profits by open-pit mining. ACM has hired you to write a program that will determine the maximum profit it can achieve given the description of a piece of land.
    Each piece of land is modelled as a set of blocks of material. Block i has an associated value (vi), as well as a cost (ci), to dig that block from the land. Some blocks obstruct or bury other blocks. So for example if block i is obstructed by blocks j and k, then one must first dig up blocks j and k before block i can be dug up. A block can be dug up when it has no other blocks obstructing it.

    输入
    The first line of input is an integer N (1≤N≤200) which is the number of blocks. These blocks are numbered 1 through N.
    Then follow N lines describing these blocks. The ith such line describes block i and starts with two integers vi, ci denoting the value and cost of the ith block (0≤vi,ci≤200).
    Then a third integer 0≤mi≤N-1 on this line describes the number of blocks that block i obstructs.
    Following that are mi distinct space separated integers between 1 and N (but excluding i) denoting the label(s) of the blocks that block i obstructs.
    You may assume that it is possible to dig up every block for some digging order. The sum of values mi over all blocks i will be at most 500.

    输出
    Output a single integer giving the maximum profit that ACM can achieve from the given piece of land.

    样例输入
    5
    0 3 2 2 3
    1 3 2 4 5
    4 8 1 4
    5 3 0
    9 2 0

    样例输出
    2

    题意:有n块石头,开采每块石头有价值vi和成本ci,一些石头压着另一些石头,也就是说某些石头的开采要先开采压着他上面的石头才能被开采,问如何开采使得利润最大化。

    最大权闭合子图模板题,这个神奇的东西来自 胡伯涛 的论文《最小割模型在信息学竞赛中的应用》。

    首先建图,计算出每块石头的利润,正利润的石头全部由源点连向一条容量为利润大小的有向边,负利润的石头全部连向汇点,容量为负利润的绝对值。若i石头压着j石头,由j石头连向i石头一条容量无限大的边表示要开采j石头首先要开采i石头。

    这样从源点S跑向汇点T一个最大流【最小割】,用所有正利润的石头利润之和减去这个最小割,即最大化利润,最小割算出来的其实是必定要损失的值。

    中间无限大容量的边可以这样理解:
    要挖该石头就要走一个该石头之上的另一个石头,若该石头是与其之上的石头都是正利润,那么损失为0可以不走到汇点,同为负利润同理。
    若该石头是正利润,其上石头是负利润,那么必定会通过这条无穷大容量通路走到汇点,也就是会被其上石头的负利润所限制。
    若该石头负利润,其上石头为正利润,从负利润到正利润有一条有向边,但这条有向边无法从源点流到汇点,因此负利润不会被减到,也就是不挖下面这块石头。
    这里写图片描述

    因为最小割跑出来的是损失量,因此跑不出来时反而是最大化了利润,不需要减去任何亏损的成本,没有一条必定的亏损。

    关于建图可以这样理解,把正利润连向源点,负利润连向汇点,跑出的最大流相当于从源点开始,跑满了正利润,这样最大化了正利润,正利润较大,负利润容量较小时,被负利润所限制,跑出来的是负利润值,那么表示从正利润中减去不得不减去的负利润。也可这样理解,当开始跑满了正利润,但这些正利润是否都可取到呢?有些正利润的石头是被负利润的压着的,那么此时无限大流量的有向边就用上了,要跑到汇点要经过这些必经之路,要拿了正利润然后判断负利润。

    若“正大负小”,则流量被负利润控制,跑出来的是所有正利润要减去的负利润。
    若“正小负大”,则跑出来的是正利润,表示最优选择肯定是选取所有正利润石头,但是有的正利润石头被负利润石头压着,并且挖了压在他们上面的负利润再挖他们是亏本的,所以不如不挖,就挖没被压着或者被压了还能盈利的石头,因此减去的是挖不到的正利润石头,以及挖到某些正利润石头所付出的代价。

    【感谢mls的讲解!!!mls天下第一!!!https://blog.csdn.net/qq_38000095/

    #include<bits/stdc++.h>
    #define LL long long
    #define M(a,b) memset(a,b,sizeof a)
    #define pb(x) push_back(x)
    using namespace std;
    const int maxn=208;
    const int inf=0x3f3f3f3f;
    struct edge
    {
        int to,val,rev;
        edge(){}
        edge(int a,int b,int c)
        {
            to=a;
            val=b;
            rev=c;
        }
    };
    vector<edge>mp[maxn];
    int n;
    inline void add(int from,int to,int val)
    {
        mp[from].pb(edge(to,val,mp[to].size()));
        mp[to].pb(edge(from,0,mp[from].size()-1));
    }
    int dep[maxn];
    bool bfs()
    {
        memset(dep,-1,sizeof dep);
        queue<int >q;
        while(!q.empty())q.pop();
        dep[0]=0;
        q.push(0);
        while(!q.empty())
        {
            int tmp=q.front();
            q.pop();
            if(tmp==n+1)return true;
            for(int i=0;i<mp[tmp].size();i++)
            {
                int &to=mp[tmp][i].to,flow=mp[tmp][i].val;
                if(dep[to]==-1&&flow)
                {
                    dep[to]=dep[tmp]+1;
                    q.push(to);
                }
            }
        }
        ///if(dep[n+1]!=-1)return true;
        return false;
    }
    int dfs(int s,int t,int flow)
    {
        if(s==t)return flow;
        int pre=0;
        for(int i=0;i<mp[s].size();i++)
        {
            int &to=mp[s][i].to,val=mp[s][i].val;
            if(dep[s]+1==dep[to]&&val>0)
            {
                int tmp=min(flow-pre,val);
                int sub=dfs(to,t,tmp);
                mp[s][i].val-=sub;
                mp[to][mp[s][i].rev].val+=sub;
                pre+=sub;
                if(pre==flow)return pre;
            }
        }
        return pre;
    }
    int dinic()
    {
        int ans=0;
        while(bfs())ans+=dfs(0,n+1,inf);
        return ans;
    }
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            for(int i=0;i<=n+1;i++)mp[i].clear();
            int ci,vi,to,num,val,ans=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d%d%d",&vi,&ci,&num);
                val=vi-ci;///先计算宝石利润
                if(val>0)add(0,i,val),ans+=val;///正利润连到源点
                else add(i,n+1,-val);///负利润连到汇点
                for(int j=1;j<=num;j++)
                {
                    scanf("%d",&to);
                    add(to,i,inf);///第i个石头压着的石头,从被压的石头连向i一条有向边,容量为无限大
                }
            }
            printf("%d
    ",ans-dinic());
        }
    }
    
  • 相关阅读:
    使用云(BAE)实现android应用数据的远程存取(MySQL)
    VI键盘图
    IOS 6 UIActivityViewController详解 社交分享
    IOS 计算字体控件盖度, 设置粗体+阴影
    myeclipse中tomcat 7.0 关于64位与32位的冲突问题 ( tcnative1.dll )
    九度OJ 1001 A+B for Matrices
    网站10大常见安全漏洞及解决方案
    js网页如何获取手机屏幕宽度
    常用正则说明
    php中的线程、进程和并发区别
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135719.html
Copyright © 2011-2022 走看看