poj2728+最优比率生成树
题意:一个无向图,每条边有两个权值,h和l,要求一个生成树,使得所有边的h的和比上l的和最小。
设x[i]等于1或0, 表示边e[i]是否属于生成树.
则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<m .
为了使 r 最小, 设计一个子问题---> 让 z = ∑(benifit[i] * x[i]) - k * ∑(cost[i] * x[i]) = ∑(d[i] * x[i]) 最小 (d[i] = benifit[i] - k * cost[i]) , 并记为z(k). 我们可以把z(k)看做以d为边权的最小生成树的总权值.(如果求r最大应该使用最大生成树).
由于cost[i]>0,所以k增大则z减小,z在k上单调递减。
接下来便是二分k
当z<0,即k>∑(benifit[i] * x[i])/∑(cost[i] * x[i]).而我们要求k尽量大z才会小,这样k无上限,矛盾。
故z应>=0,由于z在k上单调递减,所以要使r最小,则z要最小,则k要最大,k不能大到使z<0,所以k最大大到使z=0.
二分k使z=0即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstdlib> 6 using namespace std; 7 struct node 8 { 9 double x,y,h; 10 }p[1100]; 11 double g[1100][1100],l[1100][1100],h[1100][1100]; 12 double dis(double x1,double y1,double x2,double y2) 13 { 14 return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); 15 } 16 const double mmax=1<<30; 17 double prim(int n) 18 { 19 int pre[1100],vis[1100],i; 20 double lowcost[1100]; 21 memset(vis,0,sizeof(vis)); 22 vis[1]=1; 23 for(i=1;i<=n;i++) 24 { 25 pre[i]=1; 26 lowcost[i]=g[1][i]; 27 } 28 int num=0; 29 double sum=0; 30 while(num<n-1) 31 { 32 double mmin=mmax; 33 int vx; 34 for(i=1;i<=n;i++) 35 { 36 if(!vis[i]&&lowcost[i]<mmin) 37 { 38 mmin=lowcost[i]; 39 vx=i; 40 } 41 } 42 vis[vx]=1; 43 num++; 44 sum+=g[vx][pre[vx]]; 45 for(i=1;i<=n;i++) 46 { 47 if(!vis[i]&&g[vx][i]<lowcost[i]) 48 { 49 lowcost[i]=g[vx][i]; 50 pre[i]=vx; 51 } 52 } 53 } 54 return sum; 55 } 56 int main() 57 { 58 int n,i,j; 59 while(scanf("%d",&n)!=EOF&&n!=0) 60 { 61 for(i=1;i<=n;i++) 62 scanf("%lf %lf %lf",&p[i].x,&p[i].y,&p[i].h); 63 double minl=mmax,minh=mmax,maxl=-mmax,maxh=-mmax; 64 for(i=1;i<=n;i++) 65 { 66 for(j=i;j<=n;j++) 67 { 68 if(i==j) 69 { 70 l[i][j]=0; 71 h[i][j]=0; 72 } 73 else 74 { 75 l[i][j]=l[j][i]=dis(p[i].x,p[i].y,p[j].x,p[j].y); 76 h[i][j]=h[j][i]=fabs(p[i].h-p[j].h); 77 if(l[i][j]>maxl) maxl=l[i][j]; 78 if(l[i][j]<minl) minl=l[i][j]; 79 if(h[i][j]>maxh) maxh=h[i][j]; 80 if(h[i][j]<minh) minh=h[i][j]; 81 } 82 } 83 } 84 double ll=minh/maxl; 85 double rr=maxh/minl; 86 while(rr-ll>1e-4) 87 { 88 double mid=(ll+rr)/2; 89 for(i=1;i<=n;i++) 90 for(j=i;j<=n;j++) 91 g[i][j]=g[j][i]=h[i][j]-mid*l[i][j]; 92 double ans=prim(n); 93 if(ans>0) ll=mid; 94 else rr=mid; 95 } 96 printf("%.3f\n",ll); 97 } 98 return 0; 99 }