POJ-2728-Desert King
http://poj.org/problem?id=2728
有N个村庄,给出每个村庄的坐标和海拔,,benifit为两点之间的距离,cost为两点的高度差,现在要求一棵树使得 cost / benift 最小,即求一个最优比例生成树
第一次遇见这种生成树,在网上找了个解法
假设sigma(h)/sigma(l)==K均值K可取,即: sigma(h)==K*sigma(l)
sigma(h)==K*(l1+l2+l3+...lm)
sigma(h)==K*l1+K*l2+K*l3+...K*lm
把原来的每个边的h都减去K*l
即hi'=hi-li'==hi-li*K
然后问题可以转换到求hi'这些边的最小生成树了
如果hi'这些边得最小生成树权值和<=0.0,说明K这个均值可取
对于k,二分求解即可
代码基本是模仿网上的
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
#define MAXN 1010
#define INF 9999999999
struct Point
{
double x,y,h;
};
Point point[MAXN];
double mat[MAXN][MAXN];
double h[MAXN][MAXN];
double l[MAXN][MAXN];
int n;
double head,tail;
double dist(Point &a,Point &b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double prim()
{
int i,j,k;
double ans=0.0,min;
int visit[MAXN];
double dis[MAXN];
for(i=0;i<n;i++)
{
dis[i]=mat[0][i];
visit[i]=0;
}
dis[0]=1;
visit[0]=1;
for(i=1;i<n;i++)
{
min=INF;
for(j=0;j<n;j++)
if(!visit[j]&&dis[j]<min)
{
min=dis[j];
k=j;
}
ans+=min;
visit[k]=1;
for(j=0;j<n;j++)
if(!visit[j]&&dis[j]>mat[k][j])
dis[j]=mat[k][j];
}
return ans;
}
int main()
{
int i,j;
double maxc,minc,maxl,minl,mid;
while(scanf("%d",&n),n)
{
maxc=-INF;
minc=INF;
maxl=-INF;
minl=INF;
for(i=0;i<n;i++)
scanf("%lf%lf%lf",&point[i].x,&point[i].y,&point[i].h);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
{
h[i][j]=h[j][i]=fabs(point[i].h-point[j].h);
l[i][j]=l[j][i]=dist(point[i],point[j]);
if(maxc<h[i][j])
maxc=h[i][j];
if(minc>h[i][j])
minc=h[i][j];
if(maxl<l[i][j])
maxl=l[i][j];
if(minl>l[i][j])
minl=l[i][j];
}
head=minc/maxl;
tail=maxc/minl;
while(tail-head>1e-4)
{
mid=(head+tail)/2;
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
mat[i][j]=mat[j][i]=h[i][j]-mid*l[i][j];
if(prim()<=0.0)
tail=mid;
else
head=mid;
}
printf("%.3f\n",head);
}
return 0;
}