Poj 2976 Dropping tests

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;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值