题意:给定N个三维平面点,每个点都有一个高度,每两个点之间的需要构边,边的距离由x,y坐标的欧几里得距离确定,边的花费有点的高度差即z值确定,现在问一个合理的生成树中,花费比上距离的最小值为多少?
解法:每一条边对应于一个高度差,设每条边的高度差为Hi,距离为Li,则要求找到一组边集满足,一如既往的,我们假设一个比例R使得有成立,那么对式子变形后有,得到这个式子后,我们就能够将边权进行修改,求一个最小生成树来判定是否满足<=0的要求。由于图是一个稠密图,所以kruskal算法超时了,改成prim后,priority_queue照样超时,最后改成最普通版的才Ac掉。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #include <vector> using namespace std; int N, idx; double R; const double INF = 1e12; const double eps = 1e-6; double D[1005][1005]; double H[1005][1005]; struct Node { int x, y, z; void read() { scanf("%d %d %d", &x, &y, &z); } }p[1005]; inline int sign(double x) { return x < -eps ? -1 : x > eps ? 1 : 0; } double dist(int x1, int y1, int x2, int y2) { return sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } double dis[1005]; char vis[1005]; struct cmp { bool operator () (const int &a, const int &b) { return sign(dis[a] - dis[b]) > 0; } }; bool Ac(double R) { double sum = 0; memset(vis, 0, sizeof (vis)); fill(dis, dis+N, 1e12); dis[0] = 0; for (int i = 0; i < N; ++i) { double Min = INF; int u; for (int i = 0; i < N; ++i) { if (!vis[i] && sign(Min-dis[i])>0) { u = i, Min = dis[i]; } } vis[u] = 1; sum += dis[u]; for (int v = 0; v < N; ++v) { if (vis[v]) continue; double ct = H[u][v]-R*D[u][v]; if (sign(dis[v]-ct) > 0) { dis[v] = ct; } } } return sign(sum) <= 0; } double bsearch(double l, double r) { double mid, ret; while (r - l >= eps) { mid = (l + r) / 2.0; if (Ac(mid)) { ret = mid; r = mid - eps; } else { l = mid + eps; } } return ret; } int main() { while (scanf("%d", &N), N) { for (int i = 0; i < N; ++i) { p[i].read(); } idx = 0; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { H[i][j] = abs(p[i].z - p[j].z); D[i][j] = dist(p[i].x, p[i].y, p[j].x, p[j].y); } } printf("%.3f\n", bsearch(0, 1e7)); } return 0; }