例:给出
思路:
首先如果选取
目标:求
定义函数,
令:
假设我们已知在存在一个方案
即
说明了这组方案可以得到一个比现在的
显然:
存在一个临界的
这个时候的
如何求解
二分法即可。
L:=...;R:=...;
Repeat
Mid:=(L+R)/2;
For I=1..X do D[i]:=A[i]-Mid*B[i];//根据Mid计算D数组
if 检查(Mid)成功 then L:=Mid else R:=Mid;
Until abs(L-R)<Eps;
改进
Dinkelbach算法
因为
求 01 分数规划的另一个方法就是,基于这样的一个思想,并不会去二分答案,而是先随便给定一个答案,然后根据更优的解不断移动答案,逼近最优解。
L:=随便什么东西;
Repeat
Ans:=L;
For I=1..X do D[i]:=A[i]-L*B[i];//根据L计算D数组
检查解并记录;
p:=0;q:=0;
for I=每一个元素 do
如果元素I在解中
begin
p:=p+A[i];q:=q+A[i];
end;
L:=p/q;//更新解
Until abs(Ans-L)<Eps;
模板(POJ2976)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
double a[1010],b[1010];
double d[1010],eps=1e-6,ans;
int main()
{
while(scanf("%d%d",&n,&k)!=EOF&&(n|k))
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lf",&b[i]);
double l=0.0;
double r=1.0;
while((r-l)>eps)
{
double mid=(l+r)/2;
for(int i=1;i<=n;i++)
d[i]=a[i]-b[i]*mid;
sort(d+1,d+n+1);
ans=0;
for(int i=k+1;i<=n;i++)
ans+=d[i];
if(ans>=0)
l=mid;
else
r=mid;
}
l*=100.0;
printf("%.0lf\n",l);
}
return 0;
}