HDU 4009-Transfer water

Transfer water

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

题目链接:点击打开链接



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

题意:
小a住的村子决定今年把整个村庄搬到附近的山上。山上没有泉水,所以家家户户只能挖井或从其他家庭建造一条水线。如果家庭决定挖井,井的钱是他们房子的高度乘以每米X美元。如果家庭决定从其他家庭建造一条水线,如果供水家的高度不低于需水家的高度,那么一条水线的钱就等于两户的曼哈顿距离乘以每米的Y美元。
或者,如果供水家的高度低于需水家的高度,除了水管,则还需要水泵。一个水泵要用Z美元。此外,一些家庭可能不允许其他家庭在自己这里建造一条水线。现在考虑到每个家庭的3个维度(a,b,c) ,c 这意味着高度,如果每个家庭都能有水,希望能计算出整个村庄需要的最少的钱,否则告诉领导者不能做到。
输入是多组测试数据。
每个案例的第一行包含4个整数n(1 <=n<= 1000),家庭数量,X(1 <=X<= 1000),Y(1 <=Y<= 1000),Z(1 <=Z<= 1000)。
接下来n行包含3个整数a,b,c表示第i个家庭的位置,数据大小没有一个超过1000。
接下来的n行描述了家庭之间的关系。n+i+1 行描述i第i个家庭的关系。这一行将从一个整数 k 开始,接着会输入 k 个整数代表可以在第i个家庭中建立一条水线的家庭的编号。
如果n =X=Y=Z= 0,则结束输入。
如果能让每个家庭都有水,输出全村最小需要的钱的金额,如果计划不存在,就在一行中打印“poor XiaoA”。

分析:
 首先说一下本题提到的 Manhattan distance  (曼哈顿距离),A  (x1,  y1,  z1)  到 B(x2,  y2,  z2)  的曼哈顿距离就是 |x2‐x1|+|y2‐y1|+|z2‐z1|. 
本题用的是最小树形图来做,但是真的好巧妙呀,因为我们不知道在哪挖井,所以我们可以虚拟出一个虚根0,并且让它与每一个点的权值都是该点挖井的费用。然后直接就是套模板了,edge[maxn*maxn];这个数组要开大些,第一次因为开小了就  gg了,好啦,宝宝们看代码吧,因为是套上一题的模板(代码中的很多注释还保留着的)。
最后说一点,我感觉不可能完成不了计划,因为可以随意的打井,只是花费的多少罢了,好啦,宝宝们在思考思考吧!


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#define inf 0x3f3f3f3f
using namespace std;

const int maxn = 1005;
int N,X,Y,Z;
int closest[maxn],in[maxn],vis[maxn],id[maxn];

struct Point{///存居民家的三维坐标
    int x,y,z;
}point[maxn];

struct Edge
{
    int s;
    int t;
    int cost;
}edge[maxn*maxn];

int Distance(struct Point a,struct Point b)///计算曼哈顿距离
{
    return fabs(a.x-b.x)+fabs(a.y-b.y)+fabs(a.z-b.z);
}

int Min_Cost(int root,int nodenum,int edgenum)
{
    int ans = 0;
    int i;
    while(true)
    {
        for(i = 0; i < nodenum; i++)
            in[i] = inf;///先将所有节点的入边权值都刷为无穷大
        for(i = 0; i < edgenum; i++)
        {
            int s = edge[i].s;
            int t = edge[i].t;  ///s->t这条边
            if(edge[i].cost < in[t] && s!=t)
            {
                closest[t] = s;///记录与t最近的点,也是t点的入边的始点
                in[t] = edge[i].cost;///记录t的最小入边权值
            }
        }///找某个节点的入边最小权值
        int cnt = 0;
        memset(vis,-1,sizeof(vis));
        memset(id,-1,sizeof(id));
        in[root] = 0;///将根节点的入边权值刷为0,防止会出现图中所有的点一起连成一个大环
        for(i = 0; i < nodenum; i++)
        {
            ans += in[i];///第一遍先将所有点的入边权值都加上
            int t = i;
            while(vis[t]!=i && id[t]==-1 && t != root)
            /**如果该点未被标记,并且没有新的编号并且该点不是根节点,就一直找它相邻最近的点,
            一层一层的找,如果有环肯定会找到自身,因为之前第一次经过自己的时候已经被标记了,
            再次回到自身就不满足条件,就要跳出了。如果没有环,一定会找到根节点,遇见根节点也会跳出**/
            {
                vis[t] = i;
                t = closest[t];
            }
            if(t != root && id[t] == -1)
            /**如果该点是根节点就说明该图没有环,如果不是根节点,就说明该图是有环的,
            并且该环没有被赋予新的编号**/
            {
                for(int s = closest[t]; s != t; s = closest[s])
                {
                    id[s] = cnt;///将该环中的所有点都重新赋予新的编号,并且编号都一样,将它们看做一个超大点
                }
                id[t] = cnt++;
            }
        }
        if(cnt == 0)
            break;///如果此时的cut任然为0,说明该图已经没有环,那么ans就是最小树形图的值了
        for(i = 0; i < nodenum; i++)///如果没有跳出就代表该图还是有环的,就要重新将本图的点赋予新的 编号
            if(id[i] == -1)
                id[i] = cnt++;
        for(i = 0; i < edgenum; i++)
        {
            int s = edge[i].s;
            int t = edge[i].t;
            edge[i].s = id[s];
            edge[i].t = id[t];
            if(s != t)
            /**删边,这句代码真的很难理解,但我还是想尽自己的理解给大家解释一番,
            就是如果有环的话那么我们肯定多算了一条边,就要在环里的点的入边权值上扣掉这些,
            如果两个点都不是环里的点,减去最小入边权值最小为0,而我们在下一次寻找最小入边权值的话就会找到这个0,
            加在ans上相当于没有加,是因为第一次已经加上了。**/
                edge[i].cost = edge[i].cost - in[t];
        }
        nodenum = cnt;///更新新的节点个数
        root = id[root];///更新新的根节点
    }
    return ans;
}

int main()
{
    while(~scanf("%d %d %d %d",&N,&X,&Y,&Z)&&(N+X+Y+Z))
    {
        int k=0;
        for(int i=1;i<=N;i++)
        {
            scanf("%d %d %d",&point[i].x,&point[i].y,&point[i].z);
        }
        for(int i=1;i<=N;i++)///存将虚拟根节点到各个节点的权值刷为该户打井需要的费用
        {
            edge[k].s = 0;
            edge[k].t= i;
            edge[k].cost = X*point[i].z;
            k++;
        }
        for(int i = 1; i <= N; i++)
        {
            int t,node;
            scanf("%d",&t);
            for(int j=1;j<=t;j++)
            {
                scanf("%d",&node);
                edge[k].s = i;
                edge[k].t = node;
                edge[k].cost = Distance(point[i],point[node])*Y;
                if(point[i].z<point[node].z)///如果水源的地势比较低,还需要加买水垒的钱
                    edge[k].cost += Z;
               k++;
            }
        }
        int ans = Min_Cost(0,N+1,k);
        if(ans < inf)
            printf("%d\n",ans);
        else
            printf("poor XiaoA\n");
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值