1. 程式人生 > >Desert King(最優比率生成樹)

Desert King(最優比率生成樹)

【題意】 有N個村莊(N<=1000)這些村莊在不同座標和海拔,現在要對所有村莊供水,每兩個村莊之間只有一條通道即可。建造通道的距離為村莊之間的歐幾里德距離,費用則為村莊間的海拔之差。現在要求一種方案使得總費用與總距離的比值最小,問你最小的比值。

【思路】 用cost[i][j]表示 ij 村莊修造通道的費用,len[i][j]ij 村莊修造通道的距離。

方法一:二分法

設最小比值

o=cost[i][j]len[i][j]

建構函式

Map[i][j]=cost[i][j]olen[i][j]

則取最小比值時,有 Map[i][j]=0(其中 Map[i][j]裡面的 i>j這條邊是當前生成樹的邊)

實現過程: 列舉比值mid,求出所有的Map值。然後跑一次prim,求出構造最小生成樹的Map值總和ans, 當列舉的值mid就是最優值o的時候,有Map[i][j]=0 在列舉過程中,若ans<0,說明 o 值過大;若

ans>0,說明 o值過小。這個題直接寫裸的prim比用堆優化過的還快,不知道為啥.

#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; }