HDU_4009

题意

n户人家,每家对应一个三维坐标(x, y, z),z代表这家的海拔高度。

每家可以通过挖井获得水,也可以通过从别的人家引一条水渠获得水,其花费如下:

1.挖井花费z*X;

2.从不比自己低的人家引水渠花费Y*两家之间的曼哈顿距离;

3.从比自己低的人家引水渠与2中的相同,但需要多花Z来购买一个水泵

此外有的人家不允许一些人家从他们家引水渠

求使得所有人家都能获得水的最少花费

题解

首先将题目抽象为有向图,可知题目与最小树形图有关。

然后假设一个新的点作为树形图的根节点,其到其他节点的距离为每户人家挖井所需要的花费。

如此一来,题目就转化为根确定的最小树形图问题。

代码

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int E = 2;
const int INF = 0x3f3f3f3f;
const int MAX_N = 1000;

struct Point {
	int x, y, z;
	Point(int x=0, int y=0, int z=0): x(x), y(y), z(z) {}
};

struct Edge {
	int depa, dest, dist;
	Edge(int depa=0, int dest=0, int dist=0): depa(depa), dest(dest), dist(dist) {}
};

int ManhattanDist(Point a, Point b) {
	return abs(a.x-b.x) + abs(a.y-b.y) + abs(a.z-b.z);
}

Point vertex[MAX_N + E];
Edge edge[MAX_N*MAX_N + E];
int minIn[MAX_N + E];
int id[MAX_N + E];
int huan[MAX_N + E];
int depa[MAX_N + E];

int zhuLiu(int nVertex, int nEdge, int root=0) {
	int res = 0;
	while(true) {
		memset(minIn, 0x3f, sizeof(minIn));
		for(int i=0; i<nEdge; ++i)
			if(edge[i].depa!=edge[i].dest && minIn[edge[i].dest]>edge[i].dist) {
				minIn[edge[i].dest] = edge[i].dist;
				depa[edge[i].dest] = edge[i].depa;
			}
		for(int i=0; i<nVertex; ++i)
			if(i!=root && minIn[i]==INF)
				return -1;
		memset(id, -1, sizeof(id));
		memset(huan, -1, sizeof(huan));
		minIn[root] = 0;
		int newVertex = 0;
		for(int i=0; i<nVertex; ++i) {
			res += minIn[i];
			int temp = i;
			while(huan[temp]!=i && id[temp]==-1 && temp!=root)
				huan[temp] = i, temp = depa[temp];
			if(temp!=root && id[temp]==-1) {
				for(int j=depa[temp]; j!=temp; j=depa[j]) id[j] = newVertex;
				id[temp] = newVertex++;
			}
		}
		if(newVertex == 0) break;
		for(int i=0; i<nVertex; ++i)
			if(id[i] == -1)
				id[i] = newVertex++;
		for(int i=0; i<nEdge; ++i) {
			int depa = edge[i].depa;
			int dest = edge[i].dest;
			edge[i].depa = id[depa];
			edge[i].dest = id[dest];
			edge[i].dist -= minIn[dest];
		}
		nVertex = newVertex;
		root = id[root];
	}
	return res;
}

int main() {
	int nVertex, xCost, yCost, zCost;
	while(scanf("%d%d%d%d", &nVertex, &xCost, &yCost, &zCost)!=EOF &&
		(nVertex!=0 || xCost!=0 || yCost!=0 || zCost!=0) ) {
		for(int i=1; i<=nVertex; ++i)
			scanf("%d%d%d", &vertex[i].x, &vertex[i].y, &vertex[i].z);
		for(int i=0; i<nVertex; ++i) {
			edge[i].depa = 0;
			edge[i].dest = i+1;
			edge[i].dist = xCost*vertex[i+1].z;
		}
		int nEdge = nVertex;
		for(int i=1; i<=nVertex; ++i) {
			int k;
			scanf("%d", &k);
			for(int j=0; j<k; ++j) {
				int dest;
				scanf("%d", &dest);
				edge[nEdge++] = Edge(i, dest,
										yCost*ManhattanDist(vertex[i], vertex[dest]));
				if(vertex[i].z < vertex[dest].z) edge[nEdge-1].dist += zCost;
			}
		}
		printf("%d\n", zhuLiu(nVertex+1, nEdge));
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值