版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/84839148
链接
http://poj.org/problem?id=2728
大意
给定
个点的高度和坐标
若高度差为价值,记为
若坐标差为代价,记为
试找出一个
,使得图中的一个生成树集合的
最小
数据范围:
思路
首先比较容易发现数据是具有单调性的,那么我们可以二分
而若我们需要满足题目所说的生成树,那么这棵树必然是一棵最大生成树,所以我们可以建 的边,然后求最大生成树,判断结果非负即可
由于数据比较大,所以得用
本题还可以用迭代加深继续优化,不过本蒟蒻太菜,不会。。。
代码
#include<cmath>
#include<cctype>
#include<cstdio>
#include<algorithm>
using namespace std;int n;
bool vis[1001];double dis[1001],zjs,mid,x[1001],y[1001],z[1001],maxn,minn;
const double eps=1e-6;
inline double power(double x){return x*x;}//平方
inline double f(register int i,register int j,double mid)//求权值
{
return fabs(z[i]-z[j])-sqrt(power(x[i]-x[j])+power(y[i]-y[j]))*mid;
}
inline double MST(double mid)//prim算法
{
double ans=0,minn=0x3f3f3f3f;
int u=1,id=1;
dis[1]=0;fill(dis+2,dis+1+n,minn);
vis[1]=true;fill(vis+2,vis+1+n,0);//以上皆为初始化
for(register int i=1;i<n;i++)
{
minn=0x3f3f3f3f;id=1;
for(register int v=1;v<=n;v++)
{
if(vis[v]) continue;
dis[v]=min(f(u,v,mid),dis[v]);//找到最短的那条
if(dis[v]-eps<=minn)//小于精度差即可
{
minn=dis[v];
id=v;
}
}
ans+=minn;//加上
vis[id]=1;//标记选过
u=id;//记录上次选的
}
return ans;//返回
}
signed main()
{
while(scanf("%d",&n),n)
{
minn=0x3f3f3f3f;maxn=0;
for(register int i=1;i<=n;i++)
scanf("%lf %lf %lf",x+i,y+i,z+i),maxn=max(maxn,z[i]),minn=min(minn,z[i]);//输入
mid=0.0;
for(double l=0.0,r=n*(maxn-minn);r-l>eps;)//二分
{
mid=(l+r)/2.0;
zjs=MST(mid);
if(fabs(zjs)<eps) break;//到达要求,退出
if(zjs<0) r=mid;else l=mid;//更新边界
}
printf("%0.3f\n",mid);
}
}