hdu 3996 Gold Mine【最大权闭包-----最小割最大流Dinic】

来源:互联网 发布:监控网络接入设备 编辑:程序博客网 时间:2024/06/11 22:39

Gold Mine

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2700    Accepted Submission(s): 592

Problem Description

Long long ago, there is a gold mine.The mine consist of many layout, so some area is easy to dig, but some is very hard to dig.To dig one gold, we should cost some value and then gain some value. There are many area that have gold, because of the layout, if one people want to dig one gold in some layout, he must dig some gold on some layout that above this gold's layout. A gold seeker come here to dig gold.The question is how much value the gold he can dig, suppose he have infinite money in the begin.

Input

First line the case number.(<=10)

Then for every case:
  one line for layout number.(<=100)
  for every layout
  first line gold number(<=25)
  then one line for the dig cost and the gold value(32bit integer), the related gold number that must be digged first(<=50)

then w lines descripte the related gold followed, each line two number, one layout num, one for the order in that layout
see sample for details

Output

Case #x: y.
x for case number, count from 1.
y for the answer.

Sample Input

1

2

1

10 100 0

2

10 100 1

1 1

10 100 1

1 1

Sample Output

Case #1: 270

Source

2011 Multi-University Training Contest 16 - Host by TJU

 

题目大意:有t组数据,每组数据有n个区域,接下来n个区域,每个区域有m个金矿,给出每个金矿的建设使用花费以及采矿获得利润,以及想要建设当前金矿并且获得利润需要先建设的kk个金矿 ,问最大利润。


输入分析:

第一行输入一个t,第二行输入一个n,对应每一个区域,有n个金矿,每个金矿首先输入一行,cost,val,kk。表示建设当前金矿的花费,利润以及建设当前金矿需要先建设kk个金矿,接下来kk行,每行两个元素,表示那个金矿的区域号和这个金矿在那个区域是第几个金矿。


思路:


1、首先,假设我们没有这kk限制,就是一个裸的最大权闭包的问题,其建图如下:

①建立源点S,连入各个金矿,其权值设定为该金矿能够获得的利润。

②建立汇点T,将各个金矿点连入汇点,其权值设定为该金矿的建设使用价值。


按照此图及规则建图跑得的最大流即最小割,其最大利润==所有金矿能够获得的利润和-maxflow(最小割);


2、那么这样想:如果在没有kk这个限制的条件下,我们从图中拿出一条增广路:

S----------->1-------->T,其当前增广路的一个割就是S---------->1 的权值或者是1----------->T的权值中小的那个,如果s---1是瓶颈边,其实就是表示建设的花费比较大,大于了利润,所以我们拿利润,在最后所有金矿能够获得的利润和-maxflow(最小割)的过程中,其实就是相当于1号这个节点我们不建立了。如果1-------2是瓶颈边,那么其实就是利润比较大,所以我们拿建设花费,在最后所有金矿能够获得的利润和-maxflow(最小割)的过程中,其实就是相当于建立了这个点。


3、那么加上了kk这个限制条件,无非就是让我们多一种考虑的角度:

是否能够保证建立当前点之前的那些个点之后再建立当前节点是赚的。那么我们建图如下:



新增加两条曲线,其权值设定为INF即可。

那么2---------1这种曲线的意义呢?

考虑这样一种情况:


显然,其解为:40

考虑没有曲线时的最大流10+100(然后我们最后讨论1------2这种曲线的含义):


显然,我们当前取决的方案是:建设节点1,不建设节点2,但是因为这条INF曲线的存在,我们最后一条增广路S----1-----2------T,瓶颈边是2----T,将从2到T的这条边的流全部吃下,那么其实就相当于:当建立节点1的时候剩余的流量为90(剩余的利润),而且这90的剩余利润能够使得建立节点2之后还有剩余,那么这个时候我们就强制将节点2建立出来,最终最大流:10+100+50,相当于两个节点都建立了出来,最终解ans=200-最大流=40;


那么这种曲线边的含义也就很明确了。


4、最终建边规则:

①建立源点S,连入各个金矿节点,权值设定为该金矿的利润。

②建立汇点T,将各个金矿节点连入汇点,权值设定为该金矿建立的花费。

③将各个金矿点连入各个限制点(需要将这些个限制点都建立之后才能建立当前金矿),设定权值为INF.


5、跑最大流,获得最小割,其解ans=各个金矿点的权值和-最小割(最大流)。


6、注意数据比较大,用LL,并且设定的INF要足够大,这个题还会卡数组,开大了开小了都有可能出现TLE/RE/Wa的情况。


Ac代码:

#include<stdio.h>#include<string.h>#include<queue>#include<iostream>using namespace std;#define INF 10000000000000000#define ll __int64struct node{    ll from;    ll to;    ll w;    ll next;}e[2600*60*2];ll divv[6000];ll head[6000];ll cur[6000];ll tmp[200][1000];ll num[6000];ll n,ss,tt,contz,sum;void add(ll from,ll to,ll w){    /*    if(w)    {        printf("!!:%I64d %I64d %I64d\n",from,to,w);    }*/    e[contz].to=to;    e[contz].w=w;    e[contz].next=head[from];    head[from]=contz++;}void getmap(){    contz=0;    sum=0;    memset(head,-1,sizeof(head));    ss=5000;    tt=5001;    scanf("%I64d",&n);    ll cont=0;    for(ll i=1;i<=n;i++)    {        scanf("%I64d",&num[i]);        for(ll j=1;j<=num[i];j++)        {            cont++;            tmp[i][j]=cont;            ll cost,val,kk;            scanf("%I64d%I64d%I64d",&cost,&val,&kk);            sum+=val;            add(ss,cont,val);            add(cont,ss,0);            add(cont,tt,cost);            add(tt,cont,0);            while(kk--)            {                ll x,y;                scanf("%I64d%I64d",&x,&y);                add(cont,tmp[x][y],INF);                add(tmp[x][y],cont,0);            }        }    }}ll makedivv(){    queue<ll >s;    s.push(ss);    memset(divv,0,sizeof(divv));    divv[ss]=1;    while(!s.empty())    {        ll u=s.front();        if(u==tt)return 1;        s.pop();        for(ll i=head[u];i!=-1;i=e[i].next)        {            ll v=e[i].to;            ll w=e[i].w;            if(w&&divv[v]==0)            {                divv[v]=divv[u]+1;                s.push(v);            }        }    }    return 0;}ll Dfs(ll u,ll maxflow,ll tt){    if(u==tt)return maxflow;    ll ret=0;    for(ll &i=cur[u];i!=-1;i=e[i].next)    {        ll v=e[i].to;        ll w=e[i].w;        if(w&&divv[v]==divv[u]+1)        {            ll f=Dfs(v,min(maxflow-ret,w),tt);            e[i].w-=f;            e[i^1].w+=f;            ret+=f;            if(ret==maxflow)return ret;        }    }    return ret;}ll Dinic(){    ll ans=0;    while(makedivv()==1)    {        memcpy(cur,head,sizeof(head));        ans+=Dfs(ss,INF,tt);    }    return ans;}int main(){    ll t;    ll kase=0;    scanf("%I64d",&t);    while(t--)    {        getmap();        ll ans=Dinic();        printf("Case #%I64d: %I64d\n",++kase,sum-ans);    }}/*15021150 100 0110 100 11 121100 100 01110 100 11 1*/




0 0