POJ2728 Desert King

题目链接: POJ2728 Desert King
题目大意:
\(N\) 个沙漠中的城市,给出他们的坐标和海拔,每两个城市之间建输水管道的费用为其海拔差的绝对值,要求输水管道组成一个生成树,求管道每单位长度平均费用的最小值。
多组数据, \(2\leq N\leq 1000\)

思路:
最优比率生成树模板题,类似于0/1分数规划,二分答案 \(mid\) ,在完全图上跑最小生成树(注意这里是求答案最小值),每条边的权值为费用除以 (长度 \(*mid\)) ,若边权和 \(>0\) 则往下二分,vice versa 。
由于是完全图,这里 \(kruskal\)\(O(n^2log^2n)\) 的会 \(T\) 飞,换成 \(prim\) 的时间复杂度为 \(O(n^2logn)\)

Code:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#define N 1010
#define eps 1e-6
#define Inf 0x3f3f3f3f
using namespace std;
struct village{
    int x,y,h;
}v[N];
double len[N][N],val[N][N],d[N];
int n,cost[N][N];
bool vis[N];
bool check(double mid){
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i==j)val[i][j]=Inf;
            else val[i][j]=cost[i][j]-mid*len[i][j];
        }
    }
    memset(vis,false,sizeof(vis));
    for(int i=0;i<n;i++)d[i]=Inf;
    d[0]=0;
    double tot=0;
    while(1){
        int x=-1;
        for(int j=0;j<n;j++)
            if(!vis[j]&&(x==-1||d[x]>d[j]))x=j;
        if(x==-1)break;
        vis[x]=true;
        tot+=d[x];
        for(int j=0;j<n;j++)d[j]=min(d[j],val[x][j]);
    }
    return tot>=0;
}
int main(){
    while(cin>>n&&n){
        for(int i=0;i<n;i++){
            cin>>v[i].x>>v[i].y>>v[i].h;
            for(int j=0;j<i;j++){
                double dx=v[i].x-v[j].x,dy=v[i].y-v[j].y;
                len[i][j]=len[j][i]=sqrt(dx*dx+dy*dy);
                cost[i][j]=cost[j][i]=abs(v[i].h-v[j].h);
            }
        }
        double l=0,r=10000000;
        while(l+eps<r){
            double mid=(l+r)/2;
            if(check(mid))l=mid+eps;
            else r=mid;
        }
        printf("%.3f\n",l);
    }
    return 0;
}
posted @ 2020-12-28 12:58  Neal_lee  阅读(58)  评论(0编辑  收藏  举报