hdu 3996 Gold Mine (最大权闭合子图)

Gold Mine

 

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

 

 

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

 

12110 100 0210 100 11 110 100 11 1

 

Sample Output

 

Case #1: 270

 

 

 

题意:

T组测试数据

有n个区域,

每一个布局里面都有k个金矿

下面k行,每一行表示挖金矿的花费,挖金矿的收益,以及挖金矿的前提的数量w()

下面w行,每一行有两个数字,分别表示需要先挖第i个区域里的第j个金矿(i,j表示的金矿都在当前金矿之前)

问你,你最大能获得多少的收益

 

解析:

这里用最大团闭合图的原因是我们是按照题意反的来建边的
当一个金矿z同时需要a,b,c挖完时,我们就建立z->a,z->b,z->c,这样如果你要挖z,那么一定要挖a,b,c
这个正好和最大闭合子图的性质一致,你要获得z的权值时,必须把z的所有出边连接的点放进子图中

典型的最大权闭合子图问题

最大权闭合子图所建的图时,将图中所有点权值>0的点与源点s连接,点x的值赋值到s->x的边的值上

将图中所有点权值<0的点与汇点t相连,点x的权值的绝对值附到x>t的边上,其余图中的边都赋值成+∞

最大权闭合子图是最小割分成的两个子集中源点s所在的集合减去源点s

 

 

最大权闭合子图的权值和=所有正权值点权值之和-最小割

 

最小割PPT

最小割就是最大流中,每一条从s->t的流中,选择最小的流量的那条边(即流量=容量的边),然后去掉这条边。

这样每一条最大流中的流去掉的边的集合就是最小割,从而最小割去掉的边的总容(流)量=最大流

(在Dinic算法中其实,就是每次DFS后求出来的边集中,流量最小(即流量=容量的边)的那一条边)

详解:点击打开链接   点击打开链接

注意这里32位整数,需要用long long 来输入的

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
#define INF 1223372036854775807
#define MIN(a,b) a>b?b:a
#define MAX(a,b) a>b?a:b
using namespace std;
const int MAXN = 2500+100;
const int MAXM = 250000+100;
typedef long long int lli;

typedef struct node
{
	int from;
	int to;   //u邻接边的终点
	lli cap;
	int next;     //上一条边起点是u的在Map中的下标
}node;
node map[MAXM];     //边的集合
int depth[MAXN];
int head[MAXN],cnt,Des;   //head[u]指从u出发,第一条边在map中的下标
int q[MAXN];
int lay[110][30];


void addEdge(int u,int d,lli cap)
{
	map[cnt].from=u;
	map[cnt].to=d;
	map[cnt].cap=cap;
	map[cnt].next=head[u];    //指向上一条边起点是u的在Map中的下标
	head[u]=cnt;
	cnt++;
	//反向边
	map[cnt].from=d;
	map[cnt].to=u;
	map[cnt].cap=0;
	map[cnt].next=head[d];
	head[d]=cnt;
	cnt++;

}
int BFS(int s,int d)
{
	int tmp,i,front,rear;
	memset(depth,0,sizeof(depth));
	front=rear=0;
	depth[s]=1;
	q[rear++]=s;
	while(front<rear)
	{
		tmp=q[front];
		front++;
		for(i=head[tmp];i!=-1;i=map[i].next)   //i表示从tmp出发的边在map中的下标
		{
			if(map[i].cap>0&&depth[map[i].to]==0)
			{
				depth[map[i].to]=depth[tmp]+1;
				if(map[i].to==d)return 1;
				q[rear++]=map[i].to;
			}
		}
	}
	return 0;
}

lli DFS(int s,int d,lli cap)
{
	if(s==d||cap==0)return cap;
	lli r=0;
	for(int i=head[s];i!=-1;i=map[i].next)
	{
		if(depth[map[i].to]==depth[s]+1&&map[i].cap>0)
		{
			lli tmp=DFS(map[i].to,d,MIN(map[i].cap,cap-r));
			if(tmp>0)
			{
				map[i].cap-=tmp;
				map[i^1].cap+=tmp;     //i为偶数+1,i为奇数-1,map顺序是正反正反.....(偶为正,奇为反)
				r+=tmp;
				if(r==cap)
					break;
			}

		}
	}
	if(r==0)depth[s] = 0;
	return r;
}


lli sum;
lli Dinic()
{
	lli flow;
	flow=0;
	//memset(depth,0,sizeof(depth));
	while(BFS(0,Des))
	{
		flow+=DFS(0,Des,INF);
	}
	return flow;
}

int main()
{
    int t,m,n;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        cas++;
        memset(head,-1,sizeof(head));
        sum=cnt=0;
        n=1;
        scanf("%d",&m);
        Des=2502;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&lay[i][0]);
            for(int j=1;j<=lay[i][0];j++)
            {
                int z;
                lli x,y;
                scanf("%lld%lld%d",&x,&y,&z);
                lay[i][j]=n++;
                y=y-x;
                if(y>0)
                {
                    addEdge(0,lay[i][j],y);
                    sum+=y;
                }
                else if(y<0)
                {
                    addEdge(lay[i][j],2502,-y);
                }
                for(int k=1;k<=z;k++)
                {
                    int p,q;
                    scanf("%d%d",&p,&q);
                    addEdge(lay[i][j],lay[p][q],INF);
                }
            }
        }

        lli maxf=Dinic();
        printf("Case #%d: %lld\n",cas,sum-maxf);


    }
    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值