01分数规划

  已经接触过01分数规划但是只知道二分写法(实际求解略慢),Dinkelbach算法还是值得一学的。

上一道裸的01分数规划吧。POJ

x数组代表我们选或者不选 0,1构成

R=sigma(a[i]*x[i])/sigma(b[i]*x[i])

变形 设F(v)  为  sigma(a[i]*x[i])/sigma(b[i]*x[i])不小于v

那么sigma(a[i]*x[i])/sigma(b[i]*x[i])>=v

变形 sigma(a[i]*x[i]) >= sigma(b[i]*x[i]*v)

sigma(a[i]*x[i] - b[i]*x[i]*v) >= 0

那么我们定义一个数组 c[i] = a[i] - b[i]*v;

从大到小排序c[i] 再对前k个求和 如果 sigma(c[i]) >= 0说明当前的 F(v) == true;

然后我们二分答案 nlogn 复杂度

以为eps太大wa了一发很明显的话要到小数点三位那么我们的eps就不能是       1e-3

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
const int maxn = 1e3+10;
double a[maxn],b[maxn];
double c[maxn];
const double eps = 1e-4;//卡精度 
int n,k;
bool cmp(const double x,const double y)
{
    return x>y;
}
bool F(double v)
{
    for(int i=0;i<n;i++)
    {
        c[i] = a[i] - b[i]*v;
    }
    sort(c,c+n);
    double sum = 0.0;
    for(int i=0;i<n-k;i++)
    {
        sum += c[i];
    }
    return sum>=0;
}
bool  read()
{
    scanf("%d%d",&n,&k);
    if(!(n||k)) return false;    
    for(int i=0;i<n;i++)
    {
        scanf("%lf",&a[i]);
    }
    for(int i=0;i<n;i++)
    {
        scanf("%lf",&b[i]);
    }
    return true;
}
int main()
{
    while(read())
    {
        double lb = 0;
        double ub = 1;
        while(ub-lb>eps)
        {
            double mid = (lb+ub)/2;
            if(F(mid))
            {
                lb = mid;
            }
            else
            {
                ub = mid;
            }
        }
        printf("%.0f\n",lb*100);
    }
    return 0;
}
AC代码

 

posted @ 2016-10-23 14:02  zxMrlc  阅读(324)  评论(0编辑  收藏  举报