题目链接: POJ2728 Desert King
题目大意:
有 (N) 个沙漠中的城市,给出他们的坐标和海拔,每两个城市之间建输水管道的费用为其海拔差的绝对值,要求输水管道组成一个生成树,求管道每单位长度平均费用的最小值。
多组数据, (2leq Nleq 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
",l);
}
return 0;
}