poj2728:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=11063
给定(x,y,z). w[i][j]=h[i]-h[j] d[i][j]=dis(i,j) 求∑xi w[u][v] /∑xi d[u][v]最小值
定义∑xi w[u][v]=f(d) ∑xi d[u][v]=g(d) f(d)/g(d)=r最小
z=f(d)-r'g(d) 当r'=r时,z=0 当r'>r,z<0 当r'<r,z>0 所以z满足单调性。 对于r递增,z递减
所以我们可以二分这个r',直到z=0,就得到了r'=r.
话说,貌似可以迭代,不会啊QAQ
这道题里面,把二分的得到的r', 把w[u][v]-r'd[u][v]当做边权,就考虑xi(0,1)选还是不选,就是最小生成树了,此时当zmin<=0时,说明答案可以更优r'可以更小。
最小生成树用krusal要TLE,结果解释要用prim。
prim有点类似于dijistra.
prim算法:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:V
new = {x},其中x为集合V中的任一节点(起始点),E
new = {},为空;
3).重复下列操作,直到V
new = V:
b.将v加入集合V
new中,将<u, v>边加入集合E
new中;
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=1000+5;
const double inf=0x3f3f3f3f;
int x[maxn],y[maxn],h[maxn];
double w[maxn][maxn],d[maxn][maxn];
double f[maxn][maxn];
bool vis[maxn];
double dis[maxn];
int n;
double Dis(int i,int j)
{
return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j]));
}
void init()
{
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
w[i][j]=w[j][i]=abs(h[i]-h[j]);
d[i][j]=d[j][i]=Dis(i,j);
}
}
}
bool check()
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)dis[i]=inf;
dis[1]=0;
double ans=0;
while(1)
{
double minn=inf;
int ip;
for(int i=1;i<=n;i++)
{
if(vis[i])continue;
if(dis[i]<minn)
{
minn=dis[i];
ip=i;
}
}
if(abs(minn-inf)<1e-4)break;
ans+=minn;
vis[ip]=1;
for(int i=1;i<=n;i++)
{
if(vis[i])continue;
dis[i]=min(dis[i],f[ip][i]);
}
}
return ans<=0;
}
int main()
{
while(scanf("%d",&n)!=EOF&&n)
{
for(int i=1;i<=n;i++)scanf("%d%d%d",&x[i],&y[i],&h[i]);
init();
/*
要求 f(d)/g(d)=rmin
∑xiwi -r'∑xidi=z z=0 ∑xi(wi-r'di)
*/
double l=0,r=100000.0;
while(r-l>1e-4)
{
double mid=(l+r)/2;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
f[i][j]=f[j][i]=w[i][j]-mid*d[i][j];
}
}
if(check())r=mid;
else l=mid;
}
printf("%.3f\n",l);
}
return 0;
}