0-1分数规划问题是指求出解集{xi|xi=0或1}使目标(c1x1+c2x2+...+cnxn) /(d1x1+d2x2+…+dnxn)=cx/dx达到最大。
对于分数规划问题,有许多算法都能利用下面的线性目标函数解决问题。
Q(L): 最小化 cx-Ldx xi∈{0,1}
记z(L)为Q(L)的最值。令x*为分数规划的最优解,并且令L*= (cx*)/(dx*)(注:分数规划的最值)。那么下面就容易知道了:
z(L) > 0 当且仅当 L<L*
z(L) = 0 当且仅当 L=L*
z(L) < 0 当且仅当 L>L*
因此,解决分数规划问题在本质上等同于寻找L=L*使z(L)=0。出于这个目的,关于L的函数z(L)具有很多不错的性质:分段线性,凹函数,严格递减,(-nC)<0,且z(nC)>0。
根据上面的性质,显然当我们确定参量L,我们可以检验最值L*是否大于小于或等于当前的L。二分搜索答案对于浮点误差控制较好,但是效率略低。
另一种方法类似于牛顿迭代的Dinkelbach算法,二分搜索的方法的收敛速度仅仅是线性的,而Dinkelbach的收敛速度却是超线性的。
Dinkelbach算法的论述
它本质上是观察直线
z=cx’-Ldx’
于函数z(L)在L=L’处相切,这里x’是子问题Q(L’)的最优解。因此,-dx’是z(L)在L’处的斜率。而且很容易看出上面的直线与L轴相交与L=cx’/dx’.
现在我们来描述Dinkelbach对于分数规划的算法。Dinkelbach算法产生了收敛于L*的参量序列,如图1中细线所示的方式。这里我们记C=max{max|ci|,1},显然问题的最优解在区间[-nC,nC]内。
Dinkelbach算法:
步骤1:设L=L1,使 L*<=L1<=nC
步骤2:解决子问题Q(L)并得到最优解x
步骤3:如果z(L)=0,那么输出x并终止。否则,设L=cx/dx跳到步骤2
以poj3111 K Best为例,选取k个元素,使得最大。
double search(double p){ for(int i=0;i<n;i++) jw[i].value=jw[i].v-jw[i].w*p; Arrays.sort(jw); double tv=0,tw=0; for(int i=0;i<k;i++){ tv+=jw[i].v; tw+=jw[i].w; } return tv/tw; }
double ans=1,temp; while(true){ temp=search(ans); if(Math.abs(ans-temp)<0.001) break; ans=temp; }
0-1分数规划的应用:
1.最优比率生成树(poj2526 Desert King):一个带权完全图,每条边都有自己的花费值cost[i]和 长度dis[i],求一个生成树,使得r=(∑cost[i]*x[i] ) / (∑dis[i]*x[i] )最小。
解法:search时将每条边边权设为cost[i][j]-dis[i][j]*p,求最小生成树时选取的边既为一组解,不断迭代。
2.最优比率环:(poj3621 SightSeeing)
有向图中,每个点有一个权值C(1<=c<=1000),每条边有权值D(1<=D<=1000
要求找出一个环(没有重点),
环包含的点数要>=2,使得sum{C}/sum{D}最大。解法:由于此题求解的是存在性问题,不可套用Dinkelbach算法迭代,因此二分答案求解最大值,对于给定的g如果以<a,b>权重为g*D[b]-C[i]的图中存在环路,则ans>=g(注意二分double时结束条件right-left<eps,转移为left=mid、right=mid)。
3.最大密度子图
详见《最大权闭合图&&最大密度子图》