这很明显是一道 $0/1$ 分数规划问题。
这道题和普通的 $0/1$ 分数规划有什么不同呢?普通的 $0/1$ 分数规划可以不选任意多个数,而本题中我们最多不选 $k$ 个。
但这实际上并不会增加难度。
根据蓝书,普通的 $0/1$ 分数规划解法如下:
二分答案,设二分的值为 $mid$,则令 $c[i]=a[i]-mid\times b[i]$。然后将所有非负的 $c[i]$ 相加。检查加起来的这个值是否非负。
只需要将上面的做法稍微改一改:将所有的非负数选完后,设选了 $m$ 个,若 $m<n-k$,则将前 $n-k-m$ 大的负数加起来。最后检查这个值是否非负就可以。
如果普通的 $0/1$ 分数规划不懂可能难以理解。如果不懂建议看蓝书或者 OI-wiki。
我的写法比较暴力,直接将 $c$ 降序排序。这样比较方便。此时时间复杂度为 $O(n\log n\log SIZE)$。
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+5;
int n,k;
double l,r;
double a[N],b[N],c[N];
bool check(double mid){
for(int i=1;i<=n;i++){
c[i]=a[i]-mid*b[i];
}
double res=0;
sort(c+1,c+n+1);
reverse(c+1,c+n+1);
int i=1,cnt=0;
while((c[i]>=0||cnt<n-k)&&i<=n){
res+=c[i++];
cnt++;
}
return res>=0;
}
int main(){
while(cin>>n>>k&&n){
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
l=0,r=1e9;
for(int i=1;i<=50;i++){
double mid=(l+r)/2;
if(check(mid)){
l=mid;
}
else{
r=mid;
}
}
printf("%.0lf\n",100.0*l);
}
return 0;
}