(最优比率生成树)POJ 2728 - Desert King
题意:
很多村子,村子有三维坐标的属性,现在要求一个生成树,使得总高度和总宽度比率最小。
分析:
很经典的题型,可以使用二分来做,这里引用红书上的【说明】。
二分答案,假设最小的答案为best,二分答案为ans,那么我们将每条边的边权变为wi-ui*ans,则:
ans<best时,求最小生成树得到的答案>0
ans=best时,求最小生成树得到的答案=0
ans>best时,求最小生成树得到的答案<0
这里使用prim算法check一下即可。
还有一种算法是迭代法,就是初设一个值,将这个值代进去算,得到更优的解,直至完全收敛。
速度比二分快,具体需要看题。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 6 using namespace std; 7 8 #define eps (1e-6) 9 const int inf=0x3f3f3f3f; 10 const int maxn=1010; 11 int n; 12 13 double x[maxn]; 14 double y[maxn]; 15 double z[maxn]; 16 double dist[maxn][maxn]; 17 double high[maxn][maxn]; 18 19 int sgn(double x) { 20 return x < -eps ? -1 : x > eps ? 1 : 0; 21 } 22 23 double lowc[maxn]; 24 bool vis[maxn]; 25 26 double dis(double x1,double y1,double x2,double y2) { 27 return sqrt(pow(x1-x2,2)+pow(y1-y2,2)); 28 } 29 30 double check(double r) { 31 double ans=0; 32 memset(vis,false,sizeof(vis)); 33 vis[0]=true; 34 for(int i=1; i<n; i++) { 35 lowc[i]=high[0][i]-dist[0][i]*r; 36 } 37 for(int i=1; i<n; i++) { 38 double minc=inf; 39 int p=-1; 40 for(int j=0; j<n; j++) { 41 if(!vis[j]&&lowc[j]<minc) { 42 minc=lowc[j]; 43 p=j; 44 } 45 } 46 ans+=minc; 47 vis[p]=true; 48 for(int j=0; j<n; j++) { 49 double d = high[p][j]-dist[p][j]*r; 50 if(!vis[j]&&lowc[j]>d) { 51 lowc[j]=d; 52 } 53 } 54 } 55 return ans; 56 } 57 58 59 int main() { 60 while(scanf("%d",&n)&&n) { 61 for(int i=0; i<n; i++) { 62 scanf("%lf%lf%lf",&x[i],&y[i],&z[i]); 63 } 64 for(int i=0; i<n; i++) { 65 for(int j=i+1; j<n; j++) { 66 dist[i][j]=dist[j][i]=dis(x[i],y[i],x[j],y[j]); 67 high[i][j]=high[j][i]=fabs(z[i]-z[j]); 68 // printf("%f %f\n",dist[i][j],high[i][j]); 69 } 70 } 71 double left=0; 72 double right = 100; 73 while(sgn(left-right)<0) { 74 double mid = (left+right)/2; 75 double res = check(mid); 76 // printf("%f %f\n",mid,res); 77 if(sgn(res)<0) { 78 right=mid; 79 } else if(sgn(res)>0) { 80 left=mid; 81 } else { 82 break; 83 } 84 } 85 printf("%.3f\n",(left+right)/2); 86 } 87 88 return 0; 89 90 }