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());
    }
}
原文地址:https://www.cnblogs.com/kuronekonano/p/11135719.html