题意
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; }