HDU 4009 Transfer water

Transfer water

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|.
 

Source

 
题意:第一行输入n,x,y,z,再给出n个点的三维坐标(a,b,c),表示n个村庄,其中a,b,c分别表示长,宽,高。然后对于每一个村庄给出一些友好村庄,该村庄可以去友好村庄取水。现在,要使每一个村庄都能供上水。供水取决于如下两种方式:
第一种,自己挖井,花销是x乘上ci。第二种是到友好村庄中取,花销是y乘上两个村庄的哈曼顿距离,如果友好村庄海拔比自己村庄低,还要加上z。
最后问所有村庄都供上水的最小花费。
提示:哈曼顿距离为abs(ai-aj)+abs(bi-bj)+abs(ci-cj)。
 
题解:很显然,通过这题我们能很快想到,如果能构成一棵最小生成树岂不妙哉?
但是细细考虑,我们发现此题暗含一个条件就是边都是有向的。而我们知道,最小生成树的边都无向的,所以这题用最小生成树做会有瑕疵。
在考虑一下由于此题中,供水至少可以每一个村自己挖井,所以不存在无解的情况。
嗯,上一句话已经提示了一些信息,每一个村庄即使不能从别人那里取水,可以自己挖井。我们是不是可以把挖井这个条件转化一下?
显然是可以的。我们可以把挖井看成一个巨大的源点,这个源点就是我们假定的节点0。
很显然,0源点到其他n个点都有出边,同时,又没有任何点能通向0源点(因为边是有向的)。
所以此题转化为给有向带权图中指定一个特殊的点root,求一棵以root为根的有向生成树T,并且T中所有边的总权值最小。
于是这题就成了最小树形图的裸模板。因为最小树形图的定义就是给有向带权图中指定一个特殊的点root,求一棵以root为根的有向生成树T,并且T中所有边的总权值最小。
对于最小树形图算法,这里简单地提一些要点,更细的知识请自行百度。
第一步,求出每个节点能连向自己的权值最小的边,记为dis[i],并记录通过来的父节点。
第一步做完先检查一下除了根节点,是否每个点都有连向自己的边,如果有一个点没有连向自己的边,说明该图无解。
第二步,将带有环的一些点缩点,同时把每一个点的dis[i]加进ans。
第二步做完先判断一下是不是没有环,如果没有环,则求出答案ans,否则继续第三步。
第三步,构建新图,将原图处理一下,把缩好的点放入图中,并改变两个非属于同一个环中的连边权值,设该条边由u连向v,则使该边原先的权值w减掉dis[v]。这样就可以保证到最后构出来的是一棵权值和最小的有向带权树,而非带有环。该步骤有些差分维护前缀和的味道。
执行完第三步,把原图环的数量作为新图点的数量,把原图根节点所属的环记为新图的根节点。之后回到第一步做。
做完这些,就求出了最小树形图。
下面给出代码。
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1005;
 4 struct node{
 5     int a,b,c;
 6 }p[N];
 7 struct data{
 8     int u,v,w;
 9 }e[N*N];
10 int n,k,m,v,w,x,y,z,ans,dis[N],pre[N],id[N],vis[N];
11 void add(int u,int v,int w){e[m].u=u; e[m].v=v; e[m++].w=w;}
12 int get_dis(node a,node b){return abs(a.a-b.a)+abs(a.b-b.b)+abs(a.c-b.c);}
13 int Directed_MST(int root,int cnt,int sum)
14 {
15     int res=0;
16     while (1)
17     {
18         for (int i=0;i<cnt;++i) dis[i]=1e9;
19         for (int i=0;i<sum;++i)
20         if (e[i].w<dis[e[i].v]&&e[i].u!=e[i].v)
21         {
22             pre[e[i].v]=e[i].u;
23             dis[e[i].v]=e[i].w;
24         }
25         for (int i=0;i<cnt;++i)
26         {
27             if (i==root) continue;
28             if (dis[i]==1e9) return -1;
29         }
30         int tot=0;
31         memset(id,-1,sizeof(id));
32         memset(vis,-1,sizeof(vis));
33         dis[root]=0;
34         for (int i=0;i<cnt;++i)
35         {
36             res+=dis[i];
37             int v=i;
38             while (v!=root&&id[v]==-1&&vis[v]!=i) vis[v]=i,v=pre[v];
39             if (v!=root&&id[v]==-1)
40             {
41                 for (int j=pre[v];j!=v;j=pre[j]) id[j]=tot;
42                 id[v]=tot++;
43             }
44         }
45         if (!tot) break;
46         for (int i=0;i<cnt;++i)
47         if (id[i]==-1) id[i]=tot++;
48         for (int i=0;i<sum;++i)
49         {
50             int v=e[i].v;
51             e[i].u=id[e[i].u];
52             e[i].v=id[e[i].v];
53             if (e[i].u!=e[i].v) e[i].w-=dis[v];
54         }
55         cnt=tot; root=id[root];
56     }
57     return res;
58 }
59 int main()
60 {
61     while ((scanf("%d%d%d%d",&n,&x,&y,&z))&&(n+x+y+z))
62     {
63         m=0;
64         for (int i=1;i<=n;++i)
65         {
66             scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
67             add(0,i,p[i].c*x);
68         }
69         for (int i=1;i<=n;++i)
70         {
71             scanf("%d",&k);
72             for (int j=1;j<=k;++j)
73             {
74                 scanf("%d",&v);
75                 if (i==v) continue;
76                 w=get_dis(p[i],p[v])*y;
77                 if (p[v].c>p[i].c) w+=z;
78                 add(i,v,w);
79             }
80         }
81         ans=Directed_MST(0,n+1,m);
82         if (ans==-1) printf("poor XiaoA\n");
83         else printf("%d\n",ans);
84     }
85     return 0;
86 }
View Code

 

posted @ 2017-10-31 21:33  氟铷氡氦  阅读(281)  评论(0编辑  收藏  举报