题目大意就 给定n个二元组(a,b),扔掉k个二元组,使得剩下的a元素之和与b元素之和的比率最大。
关于0,1分数规划这个文章介绍的不错http://blog.csdn.net/hhaile/article/details/8883652
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])取得最值,即所有选择物品的总收益/总代价的值最大或是最小(一般题目都是规定好取K个1)(在下面先讨论最大值X)。
可以对目标式分析得到,必有一种取{ x[i] }的方式使sigma(a[i]*x[i])-X*sigma(b[i]*x[i])=0,其他的取{x[i]}的方式只能使sigma(a[i]*x[i])/sigma(b[i]*x[i])<X,当
sigma(a[i]*x[i])-X*sigma(b[i]*x[i])=0时,所以sigma((a[i]-X*b[i])*x[i])=0即把每一项(a[i]-X*b[i])*x[i],从大到小排序一下,选K个,那么这K个1就确定下来了。这样就可以下手了,可以二分出答案,比X小的X0再把每一项(a[i]-X0*b[i])*x[i]排序一下,前K大个的和一定大于0即sigma(a[i]*x[i])/sigma(b[i]*x[i])>X0,
即有比X0更优的解。若比X大的X1再把每一项(a[i]-X1*b[i])*x[i]排序一下,前K大个的和一定小于0即sigma(a[i]*x[i])/sigma(b[i]*x[i])<X1,即有比X1更优的解,从而二分出答案。
当然同样利用这种思路,可以不用二分可以用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;
本题代码:
//204K 94MS
#include<cstdio>
#include<iostream>
#include<math.h>
#include<algorithm>
#define esp 1e-7
using namespace std;
int n,k;
int v[1100],w[1100];
double y[1100];
bool ok(double x)
{
for(int i=0;i<n;i++)
y[i]=v[i]-x*w[i];
sort(y,y+n); //从小到大排序,再反向选取
double sum=0;
for(int i=k;i<n;i++) sum+=y[i];
return sum>=0;
}
int main()
{
while(~scanf("%d%d",&n,&k)&&(n||k)){
for(int i=0;i<n;i++) scanf("%d",&v[i]);
for(int i=0;i<n;i++) scanf("%d",&w[i]);
double lb=0,ub=1;
while(ub-lb>esp){
double mid=(ub+lb)/2;
if(ok(mid)) lb=mid;
else ub=mid;
}
printf("%.f\n",ub*100); //需要四舍五入
}
return 0;
}