题目链接:http://poj.org/problem?id=2728
题意:
给你n个点(x,y,z),让你求一棵生成树,使得 k = ∑ |z[i]-z[j]| / ∑ dis(i,j)最小。
|z[i]-z[j]|为一条边两端点的高度(z)之差,dis(i,j)为两端点在xy平面投影的欧几里得距离。
题解:
二分答案R。
如果当前的R还没有达到最小值ans,即R >= ans,则一定有一种方案使得 ∑ |z[i]-z[j]| / ∑ dis(i,j) <= R。
化简得:
∑ (|z[i]-z[j]| - R*dis(i,j)) <= 0
所以将|z[i]-z[j]| - R*dis(i,j)作为边权,求最小生成树。
如果求出的MST <= 0,则rig = mid,否则lef = mid。
注:此题为一个完全图,无优化Prim单次复杂度O(N^2),Kruskal和Prim+Heap单次复杂度O(N^2*log(N^2))。
然而Kruskal和Prim+Heap被卡了QAQ……
AC Code:
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> #include <vector> #include <queue> #define MAX_N 1005 #define INF_LF 1e10 #define EPS 1e-4 using namespace std; int n; bool vis[MAX_N]; double x[MAX_N]; double y[MAX_N]; double z[MAX_N]; double c[MAX_N]; double a[MAX_N][MAX_N]; double ans; void read() { for(int i=1;i<=n;i++) { scanf("%lf%lf%lf",&x[i],&y[i],&z[i]); } } void build(double r) { memset(a,0x7f,sizeof(a)); for(int i=1;i<=n;i++) { for(int j=1;j<i;j++) { double len=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); a[i][j]=a[j][i]=min(a[i][j],fabs(z[i]-z[j])-r*len); } } } double prim() { memset(c,0x7f,sizeof(c)); memset(vis,false,sizeof(vis)); c[1]=0; double res=0; while(true) { int now=-1; double minn=INF_LF; for(int i=1;i<=n;i++) { if(c[i]<minn && !vis[i]) { now=i; minn=c[i]; } } if(now==-1) break; vis[now]=true; res+=c[now]; for(int i=1;i<=n;i++) { c[i]=min(c[i],a[now][i]); } } return res; } bool check(double r) { build(r); return prim()<=0; } void solve() { double lef=0,rig=100; while(rig-lef>EPS) { double mid=(lef+rig)/2.0; if(check(mid)) rig=mid; else lef=mid; } ans=lef; } void print() { printf("%.3f\n",ans); } int main() { while(scanf("%d",&n)!=EOF) { if(n==0) break; read(); solve(); print(); } }