01分数规划问题:所谓的01分数规划问题就是指这样的一类问题,给定两个数组,a[i]表示选取i的收益,b[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得R=sigma(a[i]*x[i])/sigma(b[i]*x[i])取得最值,即所有选择物品的总收益/总代价的值最大或是最小。
01分数规划问题主要包含一般的01分数规划、最优比率生成树问题、最优比率环问题等
二分法
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算法
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;
Poj 2976
大意:给定A数组B数组,从中选择N-K个使得R最大,输出Round(100*R);
分析:限制很简单,只是数目上有所限制,处理方法也很简单,求出D数组后从大到小排列,从先前向后取N-K个即可,这时的D一定是最大的。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define eps 1e-8
#define maxn 1001
int n,m;
double a[maxn],b[maxn],d[maxn];
int c[maxn];
double doit(double L){
for(int i=0;i<n;i++) d[i]=a[i]-L*b[i];
sort(d,d+n);
double res=0;
for(int i=0;i<m;i++) res+=d[n-1-i];
return res;
}
void binary(){
double l=0,r=1000000000000;
while(l+eps<r){
double mid=(l+r)/2.0;
if(doit(mid)>=0)l=mid;
else r=mid;
}
printf("%.0lf\n",l*100);
}
bool cmp(int a,int b){ return d[a]>d[b]; }
void Dinkelbach(){
double l=1,ans;
do{
ans=l;
double p=0,q=0;
for(int i=0;i<n;i++){ d[i]=a[i]-l*b[i]; c[i]=i; }
sort(c,c+n,cmp);
for(int i=0;i<m;i++){
p+=a[c[i]];
q+=b[c[i]];
}
l=p/q;
}while(abs(l-ans)>eps);
printf("%.0lf\n",l*100);
}
int main(){
int k;
while(scanf("%d%d",&n,&k),n||k){
for(int i=0;i<n;i++)scanf("%lf",&a[i]);
for(int i=0;i<n;i++)scanf("%lf",&b[i]);
m=n-k;
// binary();
Dinkelbach();
}
return 0;
}