浅谈01分数规划

首先引出0/1分数规划的概念

给出一列数$a_i$与$b_i$,构造出一列数$x_i$,使得$\frac{\sum^{n}_{i=1}{a_i*{x_i}}}{\sum^{n}_{i=1}{b_i*{x_i}}}$最大,求出这个最大值(满足$x_i\in\lbrace{0,1}\rbrace$)

想要求出这个最大值一般使用二分答案

即当当前枚举的答案是$mid$时

判断是否有$\frac{\sum^{n}_{i=1}{a_i*{x_i}}}{\sum^{n}_{i=1}{b_i*{x_i}}}\geq mid$

如果有则$l=mid$,否则$r=mid$

那么如何快速求出不等式的左边呢?我们将原不等式变形

$$ \sum^n_{i=1}{a_i*x_i}\geq {mid*\sum^n_{i=1}{b_i*x_i}} $$

$$ \sum^n_{i=1}{a_i*x_i}-{mid*\sum^n_{i=1}{b_i*x_i}}\geq 0 $$

不等式左边将$x_i$提出去

$$x_i*(\sum_{i=1}^n{a_i-mid*b_i})\geq 0$$

因此我们可以在$O(n)$的时间内将$a_i-mid*b_i$处理出来,然后做一个简单的贪心

如果这个值$\geq 0$,那么令这个$x_i=1$,否则令$x_i=0$

关于总的时间复杂度,二分答案$O(logn)$,检验答案合法性$O(n)$,总时间复杂度为$O(nlogn)$

一道例题

题目链接http://poj.org/problem?id=2976

看到式子就知道这大概是个模板题了

唯一的不同是这里只要求选出$n-k+1$个数

那么只要将所有的$\sum_{i=1}^n{a_i-mid*b_i}$排序,直接选取前$n-k+1$个数相加即可

由于保证$a_i<b_i$,所以二分边界只要是$[0,1]$即可

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int a[1010],b[1010],n,k;
double sum[1010];

bool check(double num)
{
    int i;
    for (i=1;i<=n;i++) sum[i]=(double)a[i]-num*b[i];
    sort(sum+1,sum+1+n);
    double s=0.0;
    for (i=k+1;i<=n;i++) s+=sum[i];
    if (s>=0) return 1;else return 0;
}

int main()
{
    scanf("%d%d",&n,&k);
    while ((n!=0) || (k!=0))
    {
        int i;
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        for (i=1;i<=n;i++) scanf("%d",&b[i]);
        double l=0.0,r=1.0;
        while (r-l>1e-4)
        {
            double mid=(l+r)/2;
            if (check(mid)) l=mid; else r=mid;
        }
        printf("%.0lf\n",l*100);
        scanf("%d%d",&n,&k);
    }
    return 0;
}







猜你喜欢

转载自www.cnblogs.com/zhou2003/p/9763065.html