最佳比率生成树
最小化 ∑ a i ∑ b i = r \frac{\sum a_i}{\sum b_i}=r ∑bi∑ai=r
则 ∑ a i − r ∗ ∑ b i = 0 \sum a_i-r*\sum b_i=0 ∑ai−r∗∑bi=0
显然对于不同的边集, ∑ a i \sum a_i ∑ai和 ∑ b i \sum b_i ∑bi各不相同
设函数 f ( r ) = A − B ∗ r f(r)=A-B*r f(r)=A−B∗r
不同的边集构成了不同的截距 A A A和斜率 − B -B −B
斜率为负,截距为正,设这些直线的集合是 S S S
我们想求的是做靠左边的与 x x x轴的交点,那才是最小值!!
若作一条直线 x = p o s x=pos x=pos,会和 S S S中所有直线产生交点
如果 S S S中存在直线和 x = p o s x=pos x=pos的交点是负数,说明最小值在左边,往左边继续二分
如果都是正数,最小值在右边,往右边二分
所以,二分的时候求最小生成树判断,是不是所有的交点都是正数
#include <algorithm>
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;
const int maxn=1009;
const double eps=1e-5;
int n,pre[maxn],vis[maxn];
double s[maxn],x[maxn],y[maxn],z[maxn];
double a[maxn][maxn],b[maxn][maxn],mp[maxn][maxn];
double aabs(double x){ return x<0?-x:x;}
bool isok( double mid )
{
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
mp[i][j]=mp[j][i]=a[i][j]-mid*b[i][j];
fill(vis+1,vis+n+1,0);
double res=0;
s[1]=0,vis[1]=1;
for(int i=1;i<=n;i++)s[i]=mp[1][i];
for(int i=1;i<n;i++){
int u=0;
for(int j=1;j<=n;j++)if(!vis[j]&&(!u||s[j]<s[u]))u=j;
vis[u]=1,res+=s[u];
for(int v=1;v<=n;v++)if(!vis[v])s[v]=min(s[v],mp[u][v]);
}
return res<0;
}
int main()
{
while( cin >> n && n )
{
double l=0,r=0;
for(int i=1;i<=n;i++)
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
a[i][j] = aabs( z[i]-z[j] );
b[i][j] = sqrt( (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
r = max( r,a[i][j]/b[i][j] );
}
double ans,mid;
while( r>l+eps )
{
mid = (l+r)/2;
if( isok(mid) ) r=mid-eps,ans=mid;
else l=mid+eps;
}
printf("%.3lf\n",l);
}
}