POJ 2728 Desert King:最优比率生成树

题目链接:http://poj.org/problem?id=2728

题意:

  给你n个点(x,y,z),让你求一棵生成树,使得 k = ∑ |z[i]-z[j]| / ∑ dis(i,j)最小。

  |z[i]-z[j]|为一条边两端点的高度(z)之差,dis(i,j)为两端点在xy平面投影的欧几里得距离。

 

题解:

  二分答案R。

  如果当前的R还没有达到最小值ans,即R >= ans,则一定有一种方案使得 ∑ |z[i]-z[j]| / ∑ dis(i,j) <= R。

  化简得:

    ∑ (|z[i]-z[j]| - R*dis(i,j)) <= 0

  所以将|z[i]-z[j]| - R*dis(i,j)作为边权,求最小生成树。

  如果求出的MST <= 0,则rig = mid,否则lef = mid。

  注:此题为一个完全图,无优化Prim单次复杂度O(N^2),Kruskal和Prim+Heap单次复杂度O(N^2*log(N^2))。

    然而Kruskal和Prim+Heap被卡了QAQ……

AC Code:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#define MAX_N 1005
#define INF_LF 1e10
#define EPS 1e-4

using namespace std;

int n;
bool vis[MAX_N];
double x[MAX_N];
double y[MAX_N];
double z[MAX_N];
double c[MAX_N];
double a[MAX_N][MAX_N];
double ans;

void read()
{
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
    }
}

void build(double r)
{
    memset(a,0x7f,sizeof(a));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            double len=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
            a[i][j]=a[j][i]=min(a[i][j],fabs(z[i]-z[j])-r*len);
        }
    }
}

double prim()
{
    memset(c,0x7f,sizeof(c));
    memset(vis,false,sizeof(vis));
    c[1]=0;
    double res=0;
    while(true)
    {
        int now=-1;
        double minn=INF_LF;
        for(int i=1;i<=n;i++)
        {
            if(c[i]<minn && !vis[i])
            {
                now=i;
                minn=c[i];
            }
        }
        if(now==-1) break;
        vis[now]=true;
        res+=c[now];
        for(int i=1;i<=n;i++)
        {
            c[i]=min(c[i],a[now][i]);
        }
    }
    return res;
}

bool check(double r)
{
    build(r);
    return prim()<=0;
}

void solve()
{
    double lef=0,rig=100;
    while(rig-lef>EPS)
    {
        double mid=(lef+rig)/2.0;
        if(check(mid)) rig=mid;
        else lef=mid;
    }
    ans=lef;
}

void print()
{
    printf("%.3f
",ans);
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        read();
        solve();
        print();
    }
}
原文地址:https://www.cnblogs.com/Leohh/p/8084537.html