这题是最小生成树的一种扩展,就是求一棵树 使得 k =sigma(cost)/ sigma(len) 最小。 其中cost 为每条边花费,len为长度。 这实际上就是一个最优比例生成树。最优比例生成树的求解使用了分数规划的方法。 我们先任取k,假设k是最小值,那么sigma(ccost)-k*sigma(len)==0 。那么我们就新建图边权 为 ccosti-k*leni 。求一次最小生成树,如果生成树权值小于0,那么书名其实k还是有减小的空间的,否则,k不可能是最小。这样我们就可以用2分写了。 不过还有一种求解手段,就是用牛顿下山进行迭代搜索。这种搜索更快。 还有一点 这题只能用prim 求最小生成树 ,kural 会tle的。
方法1. 2分
VIEW CODE
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mmax=500010;
const int mod=20071027;
const double eps=1e-8;
typedef long long LL;
struct Point
{
double x,y,z;
void read()
{
scanf("%lf %lf %lf",&x,&y,&z);
}
}P[1100];
int sgn(double x)
{
if(fabs(x)<eps)
return 0;
return x<0?-1:1;
}
int n;
double cost[1100][1100];
double len[1100][1100];
double dis(Point x,Point y)
{
return sqrt( (x.x-y.x)*(x.x-y.x) + (x.y-y.y)*(x.y-y.y) );
}
double Dis[1100];
bool vis[1100];
double prim(double t)
{
for(int i=1;i<=n;i++)
Dis[i]=1e20,vis[i]=0;
Dis[1]=0;
double ans=0.0;
for(int i=1;i<=n;i++)
{
double mink=1e20;
int e=-1;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&Dis[j]<mink)
{
mink=Dis[j];
e=j;
}
}
vis[e]=1;
ans+=Dis[e];
for(int j=1;j<=n;j++)
{
if(Dis[j]>cost[e][j]-t*len[e][j])
Dis[j]=cost[e][j]-t*len[e][j];
}
}
return ans;
}
void solve()
{
double l=0,r=100000000,mid;
while(sgn(r-l)>0)
{
mid=(l+r)/2;
double mm=prim(mid);
if(sgn(mm) == 0)
break;
if(sgn(mm) < 0)
r=mid;
else
l=mid;
}
printf("%.3lf\n",mid);
}
int main()
{
while(cin>>n&&n)
{
for(int i=1;i<=n;i++)
P[i].read();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
len[i][j]=dis(P[i],P[j]);
cost[i][j]=abs(P[i].z-P[j].z);
}
}
solve();
}
return 0;
}
方法2: 牛顿下山法
VIEW CODE
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<stdlib.h>
#include<set>
#include<ctime>
#include<cmath>
#define ex 2.7182818284590452354
#define pi acos(-1.0)
#define DC(n) printf("Case #%d:",++n)
#define SD(n) scanf("%d",&n)
#define SS(str) scanf("%s",str)
#define SDB(n) scanf("%lf",&n)
#define mm 1000000007
#define mmax 1100
#define eps 1e-5
#define inf 0x7fffffff
using namespace std;
typedef __int64 LL;
struct point
{
int x,y,z;
}P[mmax];
int n;
double cost[mmax][mmax];
double dis[mmax][mmax];
double get_dis(point x,point y)
{
return sqrt(1.0*(x.x-y.x)*(x.x-y.x)+1.0*(x.y-y.y)*(x.y-y.y));
}
double Dis[mmax];
bool vis[mmax];
int pre[mmax];
double prim(double mid)
{
for(int i=1;i<=n;i++)
{
Dis[i]=cost[1][i]-mid*dis[1][i];
pre[i]=1;
}
double Cost=0,Len=0;
memset(vis,0,sizeof vis);
vis[1]=1;
Dis[1]=0;
for(int i=1;i<n;i++)
{
double mink=inf;
int e;
for(int j=2;j<=n;j++)
{
if(!vis[j]&&Dis[j]<=mink)
{
mink=Dis[j];
e=j;
}
}
vis[e]=1;
Cost+=1.0*abs(P[e].z-P[pre[e]].z);
Len+=dis[e][pre[e]];
for(int j=1;j<=n;j++)
{
if(!vis[j]&&Dis[j]>cost[e][j]-mid*dis[e][j])
{
Dis[j]=cost[e][j]-mid*dis[e][j];
pre[j]=e;
}
}
}
return Cost/Len;
}
void Dink()
{
double l=0,ans;
while(1)
{
ans=prim(l);
if(fabs(ans-l)<=eps)
break;
l=ans;
}
printf("%.3lf\n",ans);
}
int main()
{
while(cin>>n&&n)
{
for(int i=1;i<=n;i++)
{
scanf("%d %d %d",&P[i].x,&P[i].y,&P[i].z);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cost[i][j]=1.0*abs(P[i].z-P[j].z);
dis[i][j]=get_dis(P[i],P[j]);
}
}
Dink();
}
return 0;
}