现在的位置: 首页 > 综合 > 正文

HDU4009 Transfer water 最小树形图

2013年07月04日 ⁄ 综合 ⁄ 共 4199字 ⁄ 字号 评论关闭

 

有固定根的最小树形图求法O(VE):

首先消除自环,显然自环不在最小树形图中。然后判定是否存在最小树形图,以根为起点DFS一遍即可。、、

之后进行以下步骤。

设cost为最小树形图总权值。
0.置cost=0。
1.求最短弧集合Ao (一条弧就是一条有向边)

除源点外,为所有其他节点Vi,找到一条以Vi为终点的边,把它加入到集合Ao中。

(加边的方法:所有点到Vi的边中权值最小的边即为该加入的边,记prev[vi]为该边的起点,mincost[vi]为该边的权值)

2.检查Ao中的边是否会形成有向圈,有则到步骤3,无则到步骤4。

(判断方法:利用prev数组,枚举为检查过的点作为搜索的起点,做类似DFS的操作)

3.将有向环缩成一个点。
假设环中的点有(Vk1,Vk2,… ,Vki)总共i个,用缩成的点叫Vk替代,则在压缩后的图中,其他所有不在环中点v到Vk的距离定义如下:
gh[v][Vk]=min { gh[v][Vkj]-mincost[Vkj] } (1<=j<=i)而Vk到v的距离为
gh[Vk][v]=min { gh[Vkj][v] }              (1<=j<=i)
同时注意更新prev[v]的值,即if(prev[v]==Vkj) prev[v]=Vk
另外cost=cost+mincost[Vkj] (1<=j<=i)

到步骤1.

4.cost加上Ao的权值和即为最小树形图总权值。

如要输出最小树形图较烦,没实现过。

找环O(V),收缩O(E),总复杂度O(VE)。

那幅对我有莫大帮助的流程图如下,

以上内容摘自该博客http://hi.baidu.com/yhxhqvnjombfpzq/item/5d93ef69ceb541176895e682

我下面的代码没有用DFS,而是直接从找最小入边开始。并寻找圈,如果不存在圈并且每一个点都存在一条权不为INF的入边,那么则存在一个最小树形图。如果出现一个点不存在一条入边,那么树形图不存在。

Transfer water

Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 2432    Accepted Submission(s): 901

Problem Description
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|.
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;

#define INF 0x3FFFFFF
#define MAXN 2222

struct edge
{
	int  u,v,w;
}e[99999999];

int  a,b,c; 
int n,en;
int pre[MAXN],in[MAXN],id[MAXN],vis[MAXN];
int x[MAXN],y[MAXN],z[MAXN]; 

int getdis(int i,int j)
{
	return abs(x[i]-x[j])+abs(y[i]-y[j])+abs(z[i]-z[j]);
}

void add(int u,int v,int w)
{
	e[en].u=u;
	e[en].v=v;
	e[en++].w=w;
}

int zl(int root ,int vn)
{
	int ans=0;
	int cnt;
	while(1)  
	{
		for(int i=0;i<vn;i++)
			in[i]=INF,id[i]=-1,vis[i]=-1;
		for(int i=0;i<en;i++)
		{
			if(in[e[i].v]>e[i].w && e[i].u!=e[i].v)
			{
				pre[e[i].v]=e[i].u;
				in[e[i].v]=e[i].w;
			} 
		}
		in[root]=0;
		pre[root]=root;
		for(int i=0;i<vn;i++)
		{
			ans+=in[i];
			if(in[i]==INF)//这一步可以看出是否存在这样一棵树形图,因此可以略去DFS
 				return -1; 
		}
		cnt=0; 
		for(int i=0;i<vn;i++)
		{
			if(vis[i]==-1)
			{
				int t=i;
				while(vis[t]==-1)
				{
					vis[t]=i;
					t=pre[t];
				}
				if(vis[t]!=i || t==root) continue;
				for(int j=pre[t];j!=t;j=pre[j])
					id[j]=cnt;
				id[t]=cnt++;
			}
		}
		if(cnt==0) break;
		for(int i=0;i<vn;i++)
			if(id[i]==-1)  
				id[i]=cnt++;
		for(int i=0;i<en;i++)
		{
			int u,v;
			u=e[i].u;
			v=e[i].v;
			e[i].u=id[u];
			e[i].v=id[v];
			e[i].w-=in[v];
		}
		vn=cnt;
		root=id[root];
	}
	return ans;
}

void solve()
{
	int cnt,j;
	en=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&x[i],&y[i],&z[i]);
		add(0,i,z[i]*a);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&cnt);
		for(int k=1;k<=cnt;k++)
		{
			scanf("%d",&j);
			if(i==j)
				continue;
			if(z[i]>=z[j])
				add(i,j,getdis(i,j)*b);
			else
				add(i,j,getdis(i,j)*b+c);
		}
	}
	int ans=zl(0,n+1);
	if(ans==-1)
	{
		printf("poor XiaoA\n");
		return;
	}
	printf("%d\n",ans);	
}


int main()
{
	while(scanf("%d%d%d%d",&n,&a,&b,&c)!=EOF && n+a+b+c)
		solve();
	return 0;
} 

抱歉!评论已关闭.