poj 2976 Dropping tests 01分数规划

96 篇文章 0 订阅
64 篇文章 0 订阅

题意:

给定两个长度都为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);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值