题目要求的是MIN( ∑CiXi / ∑DiXi ) Xi∈{0,1},这里xi表示生成树对边的选取,xi=1表示选了,为0表示没有选。
对每个生成树,设其比率r=∑CiXi / ∑DiXi ,可得∑CiXi - ∑DiXi * r=0,可以知道对于所有的生成树,显然有∑CiXi - ∑DiXi * min(r) >= 0,当 ∑CiXi / ∑DiXi = min(r)时,则有∑CiXi - ∑DiXi * min(r) = 0,而我们现在不知道min(r),但是我们知道它满足这个式子( ∑CiXi - ∑DiXi * min(r) = ∑xi(ci-min(r)*disi)=0,这个式子很明显是一个生成树的边权和,且当r取到MIN(r)时有∑xi(ci-min(r)*disi)=0,从而问题转化为求以ci-rate*disi为边的权的最小生成树,为什么要求最小生成树?因为当rate取得MIN(r)时有生成树的边权和为0,但是我们不知道生成树边权和为0这种情况存不存在,那么就要每次求都求最小生成树,来寻找使得生成树边权和为0存在的可能性。根据 ∑xi(ci-min(r)*disi)=0这个式子存在,我们可以设一个left=0,right为100,在这个区域用二分法求min(rate),1.当我们枚举的rate使得∑xi(ci-rate*disi)=0时,即求得的最小生成树边权和为0,那么rate=MIN(r)。2.当最小生成树的边权和>0则说明该rate对应的所有生成树的边权和都>0,则∑CiXi - ∑DiXi * rate=0不存在,这种情况是rate取小了。3.当最小生成树的边权和<0则说明该rate对应的所有生成树的边权和都<0,则∑CiXi - ∑DiXi * rate=0不存在,这种情况是rate取大了。
#include<iostream>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<iomanip>
using namespace std;
#define maxn 1005
#define inf 0x3f3f3f3f
const double eps=1e-4;
double cost[maxn][maxn],len[maxn][maxn],x[maxn],y[maxn],z[maxn];
double mst,w[maxn][maxn],dis[maxn];
int vis[maxn];
int n;
double getdis(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool prim(double r)
{
for(int i=1;i<=n;i++)
{
vis[i]=0;
dis[i]=inf;
w[i][i]=0;
for(int j=i+1;j<=n;j++)
w[i][j]=w[j][i]=cost[i][j]-r*len[i][j];
}
dis[1]=0;
mst=0;
while(1)
{
int u=-1;
double minn=inf;
for(int i=1;i<=n;i++)
if(!vis[i]&&(u==-1||minn>dis[i]))
{
u=i;
minn=dis[i];
}
if(u==-1) break;
mst+=minn;
vis[u]=1;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dis[i]>w[u][i])
{
dis[i]=w[u][i];
}
}
}
if(mst>0) return true;
return false;
}
int main()
{
std::ios::sync_with_stdio(false);
while(cin>>n)
{
if(n==0) break;
for(int i=1;i<=n;i++)
{
cin>>x[i]>>y[i]>>z[i];
}
for(int i=1;i<=n;i++)
{
cost[i][i]=len[i][i]=0;
for(int j=i+1;j<=n;j++)
{
cost[i][j]=cost[j][i]=fabs(z[i]-z[j]);
len[i][j]=len[j][i]=getdis(x[i],y[i],x[j],y[j]);
}
}
double left=0,right=100;
while((right-left)>=eps)
{
double mid=(left+right)/2;
if(prim(mid))
left=mid;
else
right=mid;
}
cout<<fixed<<setprecision(3)<<left<<endl;
}
return 0;
}