题目地址:POJ 2728
题意:将n个村庄连在一起,告诉每个村庄的三维坐标,村庄之间的距离为水平方向上的距离,花费为垂直方向上的高度差,求把村庄连接起来的最小的花费与长度之比为多少。
思路:最优比率生成树。和普通的01分数规划差不多的思路,只不过这道题的函数变成了一颗树而已。我是用二分查找来做的,迭代法的话请看这篇博客Fatedayt。话说二分确实慢,在最坏的上限的情况G++还是会T的。经检验貌似没有我想的那样的超级差的极限。。QAQ
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <map>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef __int64 LL;
const int inf=0x3f3f3f3f;
const double pi= acos(-1.0);
const double esp=1e-7;
const int maxn=1010;
int n;
struct node
{
double x,y,z;
}q[maxn];
double d[maxn];
int vis[maxn];
double dis(int i,int j,double L)
{
return fabs(q[i].z-q[j].z)-L*sqrt((q[i].x-q[j].x)*(q[i].x-q[j].x)+(q[i].y-q[j].y)*(q[i].y-q[j].y));
}
double prim(double L) {
int i,j,k,u;
double sum=0;
double Min;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
d[i]=inf;
vis[1]=1;
d[1]=0;
u=1;
for(i=2;i<=n;i++){
Min=inf;
k=0;
for(j=1;j<=n;j++){
if(vis[j]) continue;
d[j]=min(d[j],dis(u,j,L)) ;
if(Min>d[j]) {
k=j;
Min=d[j];
}
}
sum+=Min;
vis[k]=1 ;
u=k;
}
return sum;
}
int main() {
int i,j;
double low,high,mid;
double maxx,minn;
while(~scanf("%d",&n)){
if(!n) break;
low=high=mid=0.0;
maxx=-inf;
minn=inf;
for(i=1;i<=n;i++){
scanf("%lf %lf %lf",&q[i].x,&q[i].y,&q[i].z);
minn=min(minn,q[i].z);
maxx=max(maxx,q[i].z);
}
high=maxx-minn;
while(high-low>esp){
mid=(low+high)/2;
if(prim(mid)>0)
low=mid;
else
high=mid;
}
printf("%.3f\n",low);
}
return 0 ;
}