Desert King(最優比率生成樹)
阿新 • • 發佈:2018-12-10
【題意】 有個村莊()這些村莊在不同座標和海拔,現在要對所有村莊供水,每兩個村莊之間只有一條通道即可。建造通道的距離為村莊之間的歐幾里德距離,費用則為村莊間的海拔之差。現在要求一種方案使得總費用與總距離的比值最小,問你最小的比值。
【思路】 用表示 村莊修造通道的費用, 為 村莊修造通道的距離。
方法一:二分法
設最小比值
建構函式
則取最小比值時,有 (其中 裡面的 這條邊是當前生成樹的邊)
實現過程: 列舉比值,求出所有的值。然後跑一次,求出構造最小生成樹的值總和, 當列舉的值就是最優值的時候,有 在列舉過程中,若,說明 值過大;若 ,說明 值過小。這個題直接寫裸的比用堆優化過的還快,不知道為啥.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const double inf=2e9;
const double eps=1e-6;
const int maxn=1005;
int n,m;
double x[maxn],y[maxn],h[maxn];
double cost[maxn][maxn],len[maxn][maxn],g[maxn][maxn];
double le,ri,mid;
bool done[maxn];
double low[maxn];
bool check(double r){
for(int i=0;i<n;++i){
for(int j=i+1;j<n;++j){
g[i][j]=g[j][i]=cost[i][j]-r*len[i][j];
}
}
int s=0;
for(int i=0;i<n;++i){
done[i]=false;
low[i]=g[s][i];
}
done[s]=true;
double ans=0;
for(int cnt=0;cnt<n-1;++cnt){
int id=-1;
double Mind=inf;
for(int i=0;i<n;++i){
if(!done[i] && low[i]<Mind){
Mind=low[i];
id=i;
}
}
ans+=Mind;
done[id]=true;
for(int i=0;i<n;++i){
if(!done[i] && g[id][i]<low[i]) low[i]=g[id][i];
}
}
return ans>=0;
}
int main(){
while(scanf("%d",&n)==1 && n){
for(int i=0;i<n;++i) scanf("%lf%lf%lf",&x[i],&y[i],&h[i]);
le=ri=0;
for(int i=0;i<n;++i){
for(int j=i+1;j<n;++j){
cost[i][j]=cost[j][i]=fabs(h[i]-h[j]);
len[i][j]=len[j][i]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
ri=max(ri,cost[i][j]/len[i][j]);
}
}
while(le+eps<ri){
mid=(le+ri)/2;
if(check(mid)) le=mid;
else ri=mid;
}
printf("%.3f\n",le);
}
return 0;
}