题意:
给定两个长度都为n的a序列和b序列,可以去掉k对下标相同的a【i】, b【i】, 求最大的sigma(a[i])/sigma(b[i]);
解题思路:
0 1分数规划的入门题 不会0 1分数规划可以看这篇博客 http://blog.csdn.net/l123012013048/article/details/45672433
大概的思路就是,我们的目标就是求一个最大的 l=sigma(a[i])/sigma(b[i])*sigma(x[i]);x[i]的值是0,1表示是否取这对数。为了求最大的l我们可以构造一个函数f(x)=A-l*B, A就是sigma(a[i]*x[i]),B就是sigma(b[i]*x[i]), 这样的话我们发现用当前的l去求出f(x),假如f(x)>0,那么就表示A/B>l,说明有一个更大的lA/B,可以更新l,而观察函数我们就可以知道,f(x)的值只和x[i]和L有关,L越大,f(x)越小,也就是会存在一个临界的l使f(x)<0,这时就没有办法再更新,就求得答案了。
更新的过程就是我们利用当前的l去求出c[i]=a[i]-l*b[i],然后贪心的取根据c[i]排序的前n-k项,这样求出的f(x)就会尽可能大,如果f(x)>0的话就说明可以继续更新答案。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps=1e-4;
int n, k;
struct p
{
int x;
int y;
double z;
}arr[1005];
bool cmp(p a, p b)
{
return a.z>b.z;
}
double f(double ans)
{
int i, j;
for(i=0; i<n; i++)arr[i].z=arr[i].x-ans*arr[i].y;//求f(x)
sort(arr, arr+n, cmp);
double fz, fm;
fz=fm=0;
for(i=0; i<n-k ;i++)
{
fz+=arr[i].x, fm+=arr[i].y;
}
return 1.0*fz/fm;
}
int main()
{
int i, j;
while(~scanf("%d%d", &n, &k))
{
if(n==k && n==0)break;
int ma, e;
ma=-1e9-3;
for(i=0; i<n; i++)
{
scanf("%d", &arr[i].x);
}
for(j=0; j<n; j++)scanf("%d", &arr[j].y);
double l, ans=1.0*arr[0].x/arr[0].y;
while(1)
{
l=f(ans);
if(fabs(l-ans)<eps)break; //要用绝对值比较,因为一开始去的值可能大于答案值,不加绝对值第一次就会跳出
ans=l;
}
l*=100;
l+=0.5;
int a=(int)l;
printf("%d\n", a);
}
}