POJ 2976 Dropping tests(初遇0,1分数规化)

题目大意就 给定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;
}





你可能感兴趣的:(POJ 2976 Dropping tests(初遇0,1分数规化))