这题是最小生成树的一种扩展,就是求一棵树 使得 k =sigma(cost)/ sigma(len) 最小。 其中cost 为每条边花费,len为长度。 这实际上就是一个最优比例生成树。最优比例生成树的求解使用了分数规划的方法。 我们先任取k,假设k是最小值,那么sigma(ccost)-k*sigma(len)==0 。那么我们就新建图边权 为 ccosti-k*leni 。求一次最小生成树,如果生成树权值小于0,那么书名其实k还是有减小的空间的,否则,k不可能是最小。这样我们就可以用2分写了。 不过还有一种求解手段,就是用牛顿下山进行迭代搜索。这种搜索更快。 还有一点 这题只能用prim 求最小生成树 ,kural 会tle的。
方法1. 2分
VIEW CODE
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #include<queue> #include<stack> #include<string> #include<cstring> #include<map> #include<vector> #include<set> #include<ctime> #include<stdlib.h> using namespace std; const int mmax=500010; const int mod=20071027; const double eps=1e-8; typedef long long LL; struct Point { double x,y,z; void read() { scanf("%lf %lf %lf",&x,&y,&z); } }P[1100]; int sgn(double x) { if(fabs(x)<eps) return 0; return x<0?-1:1; } int n; double cost[1100][1100]; double len[1100][1100]; double dis(Point x,Point y) { return sqrt( (x.x-y.x)*(x.x-y.x) + (x.y-y.y)*(x.y-y.y) ); } double Dis[1100]; bool vis[1100]; double prim(double t) { for(int i=1;i<=n;i++) Dis[i]=1e20,vis[i]=0; Dis[1]=0; double ans=0.0; for(int i=1;i<=n;i++) { double mink=1e20; int e=-1; for(int j=1;j<=n;j++) { if(!vis[j]&&Dis[j]<mink) { mink=Dis[j]; e=j; } } vis[e]=1; ans+=Dis[e]; for(int j=1;j<=n;j++) { if(Dis[j]>cost[e][j]-t*len[e][j]) Dis[j]=cost[e][j]-t*len[e][j]; } } return ans; } void solve() { double l=0,r=100000000,mid; while(sgn(r-l)>0) { mid=(l+r)/2; double mm=prim(mid); if(sgn(mm) == 0) break; if(sgn(mm) < 0) r=mid; else l=mid; } printf("%.3lf\n",mid); } int main() { while(cin>>n&&n) { for(int i=1;i<=n;i++) P[i].read(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { len[i][j]=dis(P[i],P[j]); cost[i][j]=abs(P[i].z-P[j].z); } } solve(); } return 0; }
VIEW CODE
#include<cstdio> #include<cmath> #include<queue> #include<stack> #include<string> #include<cstring> #include<iostream> #include<map> #include<vector> #include<algorithm> #include<stdlib.h> #include<set> #include<ctime> #include<cmath> #define ex 2.7182818284590452354 #define pi acos(-1.0) #define DC(n) printf("Case #%d:",++n) #define SD(n) scanf("%d",&n) #define SS(str) scanf("%s",str) #define SDB(n) scanf("%lf",&n) #define mm 1000000007 #define mmax 1100 #define eps 1e-5 #define inf 0x7fffffff using namespace std; typedef __int64 LL; struct point { int x,y,z; }P[mmax]; int n; double cost[mmax][mmax]; double dis[mmax][mmax]; double get_dis(point x,point y) { return sqrt(1.0*(x.x-y.x)*(x.x-y.x)+1.0*(x.y-y.y)*(x.y-y.y)); } double Dis[mmax]; bool vis[mmax]; int pre[mmax]; double prim(double mid) { for(int i=1;i<=n;i++) { Dis[i]=cost[1][i]-mid*dis[1][i]; pre[i]=1; } double Cost=0,Len=0; memset(vis,0,sizeof vis); vis[1]=1; Dis[1]=0; for(int i=1;i<n;i++) { double mink=inf; int e; for(int j=2;j<=n;j++) { if(!vis[j]&&Dis[j]<=mink) { mink=Dis[j]; e=j; } } vis[e]=1; Cost+=1.0*abs(P[e].z-P[pre[e]].z); Len+=dis[e][pre[e]]; for(int j=1;j<=n;j++) { if(!vis[j]&&Dis[j]>cost[e][j]-mid*dis[e][j]) { Dis[j]=cost[e][j]-mid*dis[e][j]; pre[j]=e; } } } return Cost/Len; } void Dink() { double l=0,ans; while(1) { ans=prim(l); if(fabs(ans-l)<=eps) break; l=ans; } printf("%.3lf\n",ans); } int main() { while(cin>>n&&n) { for(int i=1;i<=n;i++) { scanf("%d %d %d",&P[i].x,&P[i].y,&P[i].z); } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { cost[i][j]=1.0*abs(P[i].z-P[j].z); dis[i][j]=get_dis(P[i],P[j]); } } Dink(); } return 0; }
poj 2728(Desert King) 最优比例生成树 (分数规划)
原文地址:http://blog.csdn.net/u012127882/article/details/43227397