zoukankan      html  css  js  c++  java
  • 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();
        }
    }
  • 相关阅读:
    深入浅出设计模式——命令模式(Command Pattern)
    深入浅出设计模式——职责链模式(Chain of Responsibility Pattern)
    深入浅出设计模式——代理模式(Proxy Pattern)
    深入浅出设计模式——享元模式(Flyweight Pattern)
    深入浅出设计模式——外观模式(Facade Pattern)
    perl字符串操作符
    perl中单引号和双引号字符串区别
    用例图详解
    perl中system函数和反引号` `使用区别
    window cmd命令行下执行perl语句
  • 原文地址:https://www.cnblogs.com/Leohh/p/8084537.html
Copyright © 2011-2022 走看看