01整数规划有两种解决方法:二分和迭代,二分效率低,迭代容易卡精度。。。。。
这个题二分的话。。貌似会花好长时间。。。。网友是这么说的。。。。然后学了一下迭代。。。。。200++过的。。。
0-1分数规划
设x[i]等于1或0, 表示边e[i]是否属于生成树.
则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<m .
为了使 r 最大, 设计一个子问题---> 让 z = ∑(benifit[i] * x[i]) - l * ∑(cost[i] * x[i]) = ∑(d[i] * x[i]) 最大 (d[i] = benifit[i] - l * cost[i]) , 并记为z(l). 我们可以兴高采烈地把z(l)看做以d为边权的最大生成树的总权值.
然后明确两个性质:
1. z单调递减
证明: 因为cost为正数, 所以z随l的减小而增大.
2. z( max(r) ) = 0
证明: 若z( max(r) ) < 0, ∑(benifit[i] * x[i]) - max(r) * ∑(cost[i] * x[i]) < 0, 可化为 max(r) < max(r). 矛盾;
若z( max(r) ) >= 0, 根据性质1, 当z = 0 时r最大.
到了这个地步, 七窍全已打通, 喜欢二分的上二分, 喜欢Dinkelbach的就Dinkelbach.
讲解转自 http://www.cnblogs.com/lotus3x/archive/2009/03/21/1418480.html
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; #define inf 1000000000.00 #define N 1010 int x[N],y[N],z[N],pre[N],vis[N]; double cst[N][N],len[N][N],maze[N][N],dis[N]; double prim(int n,double mid){ int i,j,dj; double ta=0,tb=0,k; for(i=1;i<n;i++) for(j=i+1,maze[i][i]=inf;j<=n;j++) maze[i][j]=maze[j][i]=cst[i][j]-len[i][j]*mid; for(i=1;i<=n;i++) pre[i]=1,vis[i]=0,dis[i]=maze[1][i]; dis[1]=0,vis[1]=1; for(i=1;i<n;i++){ k=inf; for(j=1;j<=n;j++) if(!vis[j] && k>dis[j]){ k=dis[j],dj=j; } ta+=cst[pre[dj]][dj]; tb+=len[pre[dj]][dj]; vis[dj]=1; for(j=1;j<=n;j++) if(!vis[j] && dis[j]>maze[dj][j]) dis[j]=maze[dj][j],pre[j]=dj; } return ta/tb; } int main(){ int n,i,j; double ans,tmp; while(scanf("%d",&n)!=-1 && n){ for(i=1;i<=n;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]); for(i=1;i<n;i++) for(j=i+1,cst[i][i]=len[i][i]=inf;j<=n;j++){ cst[i][j]=cst[j][i]=(double)abs(z[i]-z[j]); len[i][j]=len[j][i]=sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } tmp=1.0,ans=prim(n,tmp); while(fabs(ans-tmp)>0.00001){ tmp=ans,ans=prim(n,tmp); } printf("%.3lf\n",ans); } return 0; }