题目大意
题目要求我们求成本和总长度的比值的最小值。
这是一道0/1分数规划问题。
分析:
我们首先需要构建一个完全图,在该完全图上求满足题目要求的树。
假设树中每一段路径的成本是ai ,长度是bi ,则比值为
(
∑
i
=
1
n
a
)
/
(
∑
i
=
1
n
b
)
(\sum_{i=1}^{n}a)/(\sum_{i=1}^{n}b)
(∑i=1na)/(∑i=1nb)。
我们要做的就是求该式子的最小值,可以用二分来求最小值。
假设当前二分的值为mid,我们只需要判断
(
∑
i
=
1
n
a
)
/
(
∑
i
=
1
n
b
)
<
=
m
i
d
(\sum_{i=1}^{n}a)/(\sum_{i=1}^{n}b)<=mid
(∑i=1na)/(∑i=1nb)<=mid是否成立。
如果成立,我们就让r=mid,否则l=mid。
将上式变形:
(
∑
i
=
1
n
a
)
<
=
m
i
d
∗
(
∑
i
=
1
n
b
)
(\sum_{i=1}^{n}a)<=mid*(\sum_{i=1}^{n}b)
(∑i=1na)<=mid∗(∑i=1nb)
继续变形:
(
∑
i
=
1
n
a
)
−
m
i
d
∗
(
∑
i
=
1
n
b
)
<
=
0
(\sum_{i=1}^{n}a)-mid*(\sum_{i=1}^{n}b)<=0
(∑i=1na)−mid∗(∑i=1nb)<=0
所以我们只需要在check时,将边权变为成本-mid*路径长度,构建最小生成树,判断最后的最小生成树的边权之和是否小于等于0即可。
代码:
//构建完全图
//构建最小生成树
//二分
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
const double eps=1e-8;
double g[N][N],d[N][N];
double dis[N];
int n;
int st[N];
struct Node{
int x,y,h;
void read(){
cin>>x>>y>>h;
}
double get_dis(Node b){
double dx=x-b.x,dy=y-b.y;
return sqrt(dx*dx+dy*dy);
}
}cun[N];
bool check(double x){
double res=0;
double gg=0,dd=0;
memset(st,0,sizeof st);
for(int i=1;i<=n;i++)dis[i]=1e9;
dis[1]=0;
for(int i=1;i<=n;i++){
//找不在集合内且距离集合最小的点
int t=-1;
for(int j=1;j<=n;j++){
if(!st[j]&&(t==-1||dis[j]<dis[t]))t=j;
}
st[t]=1;
res+=dis[t];
for(int i=1;i<=n;i++){
if(!st[i]){
dis[i]=min(dis[i],g[t][i]-x*d[t][i]);
}
}
}
return res<=0;
}
void solve(){
for(int i=1;i<=n;i++)cun[i].read();
//构建完全图
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=abs(cun[i].h-cun[j].h);
d[i][j]=cun[i].get_dis(cun[j]);
}
}
//二分
double l=0,r=1e12;
while(fabs(l-r)>eps){
double mid=(l+r)/2;
if(check(mid))r=mid;
else l=mid;
}
printf("%.3lf\n",l);
}
int main(){
while(cin>>n){
if(n==0)break;
solve();
}
}