【POJ2728】Desert King-最优比率生成树

测试地址:Desert King
题目大意:平面上有n个点,每个点上有一个高度h(i),在两个点之间修建管道所需的费用是|h(i)h(j)|,管道的长度是两个点的欧几里得距离,要求一棵生成树使得费用和与管道总长比值最小,求这个最小比值。
做法:本题可以转化成最优比率生成树来解决。
求费用和与管道总长的最小比值,就是求管道总长与费用和的最大比值的倒数,这样就可以把问题转化为0-1分数规划中的一个经典问题——最优比率生成树来解决了,具体做法网上有比我讲得好的,这里就不赘述了,注意一下费用和为0的情况即可。本人这里用的是二分+最大生成树的做法写的,时间复杂度为O(n2logmax(r)),还有一种叫Dinkelbach的算法有待学习。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define eps 1e-6
using namespace std;
int n;
double x[1010],y[1010],h[1010],dis[1010];
bool vis[1010];

double d(int i,int j)
{
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}

bool check(double l)
{
    double z=0.0;
    memset(vis,0,sizeof(vis));
    vis[1]=1,dis[1]=0;
    for(int i=2;i<=n;i++)
        dis[i]=d(1,i)-l*fabs(h[1]-h[i]);
    for(int i=1;i<n;i++)
    {
        double mx=-200000000;
        mx*=10000.0;
        int mni;
        for(int j=1;j<=n;j++)
            if (!vis[j]&&dis[j]>mx) mx=dis[j],mni=j;
        vis[mni]=1;
        z+=dis[mni];
        for(int j=1;j<=n;j++)
            if (!vis[j]) dis[j]=max(dis[j],d(mni,j)-l*fabs(h[mni]-h[j]));
    }
    if (z<0) return 0;
    else return 1;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        bool flag=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&x[i],&y[i],&h[i]);
            for(int j=1;j<i;j++) if (h[i]!=h[j]) {flag=1;break;}
        }
        if (!flag) {printf("0.000\n");continue;}
        double l=0,r=200000000;
        while(r-l>=eps)
        {
            double mid=(l+r)/2;
            if (check(mid)) l=mid;
            else r=mid;
        }

        printf("%.3f\n",1.0/r);
    }

    return 0;
}
posted @ 2017-10-12 22:13  Maxwei_wzj  阅读(72)  评论(0编辑  收藏  举报