图论:HDU-4009 Transfer water (最小树形图模板)

XiaoA lives in a village. Last year flood rained the village. So they decide to move the whole village to the mountain nearby this year. There is no spring in the mountain, so each household could only dig a well or build a water line from other household. If the household decide to dig a well, the money for the well is the height of their house multiplies X dollar per meter. If the household decide to build a water line from other household, and if the height of which supply water is not lower than the one which get water, the money of one water line is the Manhattan distance of the two households multiplies Y dollar per meter. Or if the height of which supply water is lower than the one which get water, a water pump is needed except the water line. Z dollar should be paid for one water pump. In addition,therelation of the households must be considered. Some households may do not allow some other households build a water line from there house. Now given the 3‐dimensional position (a, b, c) of every household the c of which means height, can you calculate the minimal money the whole village need so that every household has water, or tell the leader if it can’t be done.

Input

Multiple cases. 
First line of each case contains 4 integers n (1<=n<=1000), the number of the households, X (1<=X<=1000), Y (1<=Y<=1000), Z (1<=Z<=1000). 
Each of the next n lines contains 3 integers a, b, c means the position of the i‐th households, none of them will exceeded 1000. 
Then next n lines describe the relation between the households. The n+i+1‐th line describes the relation of the i‐th household. The line will begin with an integer k, and the next k integers are the household numbers that can build a water line from the i‐th household. 
If n=X=Y=Z=0, the input ends, and no output for that. 

Output

One integer in one line for each case, the minimal money the whole village need so that every household has water. If the plan does not exist, print “poor XiaoA” in one line. 

Sample Input

2 10 20 30
1 3 2
2 4 1
1 2
2 1 2
0 0 0 0

Sample Output

30


        
  

Hint

In  3‐dimensional  space  Manhattan  distance  of  point  A  (x1,  y1,  z1)  and  B(x2,  y2,  z2)  is |x2‐x1|+|y2‐y1|+|z2‐z1|. 

 

题意描述:在一个村庄有n户人家(x,y,h),现在打算在村庄中挖井和建水渠使每户人家都可以用上水

1、如果挖井费用为高度h*X;

2、如果a->b建水渠,if(a.h>=b.h)费用为两点距离dis*Y,反之费用为dis*Y+Z

问,怎样挖井和建水渠才能使费用最小?

分析:我们可以新建一个源点指向所有的用户,权值为挖井的费用,然后根据是否能建水渠构建个用户之间的关系,求图的最小树形图即可

 

 #include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#define MAXN 1010
#define INF 0x7fffffff
using namespace std;
struct Point
{
	int x,y,z;
} p[MAXN];
struct Edge
{
	int u,v,cost;
	Edge() {}
	Edge(int u,int v,int cost):u(u),v(v),cost(cost) {}
} edge[MAXN*MAXN];
int n,m,X,Y,Z;
inline int get_cost(Point a,Point b)
{
	int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
	if(a.z>=b.z) return dis*Y;
	return dis*Y+Z;

}
int pre[MAXN];///记录当前顶点的前驱顶点
int id[MAXN];///经过缩点之后:原图顶点号->新图顶点号
int in[MAXN];///记录顶点所有入边的权值的最小值
int vis[MAXN];///是否访问过当前顶点
inline int ZhuLiu(int root,int n,int m)
{
	int u,v;
	int ret=0;///最小树形图的权值
	while(true)
	{
		for(int i=0; i<=n; ++i) in[i]=INF;
		/*记录顶点所有入边的权值的最小值*/
		for(int i=0; i<m; ++i)
		{
			u=edge[i].u;
			v=edge[i].v;
			if(edge[i].cost<in[v]&&u!=v) ///去除v->v这种入边
			{
				pre[v]=u;
				in[v]=edge[i].cost;
			}
		}
		/*除根节点外其他顶点每个点都必须有入边,如果没有则无法构成树,返回INF表示不存在*/
		for(int i=0; i<=n; ++i)
		{
			if(i==root) continue;
			if(in[i]==INF) return INF;
		}
		in[root]=0;
		int cnt=0;///对每个顶点进行重新编号
		memset(id,-1,sizeof(id));
		memset(vis,-1,sizeof(vis));
		for(int i=0; i<=n; ++i)
		{
			ret+=in[i];
			v=i;
			/*构造环*/
			while(vis[v]!=i&&id[v]==-1&&v!=root)
			{
				vis[v]=i;
				v=pre[v];
			}
			/*判断是否存在环,如果存在环则进行缩点,环中每一个顶点对应同一个顶点cnt*/
			if(v!=root&&id[v]==-1)
			{
				for(u=pre[v]; u!=v; u=pre[u]) id[u]=cnt;
				id[v]=cnt++;
			}
		}
		/*如果不存在环,则当前树即为最小树形图*/
		if(cnt==0) break;
		/*对于其他不在环中的顶点进行编号*/
		for(int i=0; i<=n; ++i)
		{
			if(id[i]==-1) id[i]=cnt++;
		}
		/*对相应的边进行修改*/
		for(int i=0; i<m; ++i)
		{
			v=edge[i].v;
			edge[i].u=id[edge[i].u];
			edge[i].v=id[edge[i].v];
			if(edge[i].u!=edge[i].v)
				edge[i].cost-=in[v];
		}
		n=cnt-1;
		root=id[root];
	}
	return ret;
}
int main()
{
	while(scanf("%d%d%d%d",&n,&X,&Y,&Z)!=EOF)
	{
		if(n==0&&X==0&&Y==0&&Z==0) break;
		for(int i=1; i<=n; ++i) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
		int k,to;
		m=0;
		for(int i=1; i<=n; ++i)
		{
			scanf("%d",&k);
			while(k--)
			{
				scanf("%d",&to);
				edge[m++]=Edge(i,to,get_cost(p[i],p[to]));
			}
		}
		for(int i=1; i<=n; ++i)
			edge[m++]=Edge(0,i,p[i].z*X);
		printf("%d\n",ZhuLiu(0,n,m));
	}
	return 0;
}

最小树形图copy过来的(原文链接https://www.cnblogs.com/xzxl/p/7243466.html

一、相关定义

定义:设G = (V,E)是一个有向图,它具有下述性质:

  1. G中不包含有向环; 
  2. 存在一个顶点vi,它不是任何弧的终点,而V中的其它顶点都恰好是唯一的一条弧的终点,则称 G是以vi为根的树形图。

最小树形图就是有向图G = (V, E)中以vi为根的树形图中权值和最小的那一个。

另一种说法:最小树形图,就是给有向带权图一个特殊的点root,求一棵以root为根节点的树使得该树的的总权值最小。

性质:最小树形图基于贪心和缩点的思想。

缩点:将几个点看成一个点,所有连到这几个点的边都视为连到收缩点,所有从这几个点连出的边都视为从收缩点连出

 

二、算法描述

【概述】

为了求一个图的最小树形图,①先求出最短弧集合E0;②如果E0不存在,则图的最小树形图也不存在;③如果E0存在且不具有环,则E0就是最小树形图;④如果E0存在但是存在有向环,则把这个环收缩成一个点u,形成新的图G1,然后对G1继续求其的最小树形图,直到求到图Gi,如果Gi不具有最小树形图,那么此图不存在最小树形图,如果Gi存在最小树形图,那么逐层展开,就得到了原图的最小树形图。

【实现细节】

设根结点为v0,

  • (1)求最短弧集合E0

  从所有以vi(i ≠ 0)为终点的弧中取一条最短的,若对于点i,没有入边,则不存在最小树形图,算法结束;如果能取,则得到由n个点和n-1条边组成的图G的一个子图G',这个子图的权值一定是最小的,但是不一定是一棵树。

  • (2)检查E0

  若E0没有有向环且不包含收缩点,则计算结束,E0就是图G以v0为根的最小树形图;若E0含有有向环,则转入步骤(3);若E0没有有向环,但是存在收缩点,转到步骤(4)。

  • (3)收缩G中的有向环

  把G中的环C收缩成点u,对于图G中两端都属于C的边就会被收缩掉,其他弧仍然保留,得到一个新的图G1,G1中以收缩点为终点的弧的长度要变化。变化的规则是:设点v在环C中,且环中指向v的边的权值为w,点v'不在环C中,则对于G中的每一条边<v', v>,在G1中有边<v', u>和其对应,且权值WG1(<v', u>) = WG(<v', v>) - w;对于图G中以环C中的点为起点的边<v', v>,在图G1中有边<u, v'>,则WG1(<u, v'>) = WG(<v', v>)。有一点需要注意,在这里生成的图G1可能存在重边。

  对于图G和G1:

  ①如果图G1中没有以v0为根的最小树形图,则图G也没有;

  ②如果G1中有一v0为根的最小树形图,则可按照步骤(4)的展开方法得到图G的最小树形图。

所以,应该对于图G1代到(1)中反复求其最小树形图,直到G1的最小树形图u求出。

  • (4)展开收缩点

  假设图G1的最小树形图为T1,那么T1中所有的弧都属于图G的最小树形图T。将G1的一个收缩点u展开成环C,从C中去掉与T1具有相同终点的弧,其他弧都属于T。

【小结】

对最小树形图做个小小的总结:

1:清除自环,自环是不可能存在于任何最小树形图中的;

2:求出每个顶点的的最小入边;

3:判断该图是否存在最小树形图,由 1 可以判定,或者以图中顶点v作为根节点遍历该图就能判断是否存在最小树形图;

4:找环,之后建立新图,缩点后重新标记。

【图示——最小树形图构造流程】

 

解读:第一幅图为原始图G,首先对于图G求其最短弧集合E0,即第二幅图G1;然后检查E0是满足条件,在这里,可以看到G1具有两个环,那么把这两个环收缩,如第三幅图所示,U1、U2分别为收缩后的点,然后将对应的权值进行更新,起点是环中的点,终点是环外的点,则权值不变。反之,起点是环外的点,终点是环内的点,则权值应该减去E0中指向环内点的权值,形成新的图,如第三幅图,对于其反复求最小树形图,直到不存在最小树形图,或者求得缩点后的图的最小树形图,然后展开就好了,如第六幅图。

如果只要求计算权值的话,则不需要展开,所有环中权值的和加上其他各个点与点之间,或者收缩点和点之间的权值就是总的权值。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值