zoukankan      html  css  js  c++  java
  • 01分数规划 基础教学篇

    01规划问题大概是给你n件物品,每个物品有价值P和体积V两个属性,让你选择其中k件,使得这k件物品的价值和除以体积和最大。

    我们可以设X[i]=0/1,如果X[i]==0说明第i件物品物品不选,如果X[i]==1说明第i件物品要选。

    那么我们要求的k件物品的价值和除以体积和R=sigma(P[i]*X[i])/sigma(V[i]*X[i]);

    我们再定义一个函数f(L)=sigma(P[i]*X[i])-L*sigma(V[i]*X[i])=sigma((P[i]-L*V[i])*X[i]);设d[i]=P[i]-L*V[i];则f(L)=sigma(d[i]*X[i]);

    假设f(L)>0,也就是sigma(P[i]*X[i])-L*sigma(V[i]*X[i])>0,变形后就是sigma(P[i]*X[i])/sigma(V[i]*X[i])>L,那么01分数规划问题就可以转化为找到最大的L使得f(L)>0;

    由于d[i]=P[i]-L*V[i]是随着L增大而递减的,因此f(L)=sigma(d[i]*X[i])也是线性的,可以直接二分寻找这个L。

    那么如何判断f(L)>0呢?我们处理出来d数组后,要在里面选择其中k个使得他们的和大于0,那么就要找前k大的数字加起来,所以排下序再记录下和就行了。

    找到L后,L也是我们所要求的答案。

    这里有道模板题: http://poj.org/problem?id=2976

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <string>
    #include <stack>
    #include <map>
    #include <set>
    #include <bitset>
    #define b first
    #define a second
    #define clr(u,v); memset(u,v,sizeof(u));
    #define in() freopen("data","r",stdin);
    #define out() freopen("ans","w",stdout);
    #define Clear(Q); while (!Q.empty()) Q.pop();
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef pair<double, double> pdd;
    typedef pair<int, int> pii;
    const int maxn = 1e4 + 10;
    const int INF = 0x3f3f3f3f;
    const double eps = 1e-6;
    pdd N[maxn];
    int n, k;
    double d[maxn];
    bool f(double L)
    {
        double sum = 0;
        for (int i = 0; i < n; i++)
            d[i] = N[i].a - L * N[i].b;
        sort(d, d + n);
        for (int i = n - 1; i >= n - k; i--)
            sum += d[i];
        return sum > 0;
    }
    
    double solve(double L, double R)
    {
        double mid;
        while (R - L > eps)
        {
            mid = (L + R) / 2;
            if (f(mid)) L = mid;
            else R = mid;
        }
        return mid;
    }
    
    int main()
    {
        while(scanf("%d%d", &n, &k), n || k)
        {
            double Mx = 0;
            k = n - k;
            for (int i = 0; i < n; i++) scanf("%lf", &N[i].a);
            for (int i = 0; i < n; i++)
            {
                scanf("%lf", &N[i].b);
                Mx = max(Mx, N[i].a / N[i].b);
            }
            printf("%.0f
    ", solve(0, Mx) * 100);
        }
        return 0;
    }
  • 相关阅读:
    Linux日志文件utmp、wtmp、lastlog、messages
    Linux日志五大命令详解
    php 函数合并 array_merge 与 + 的区别
    MySQL对数据表进行分组查询(GROUP BY)
    如何在mysql中查询每个分组的前几名
    Autojump:一个可以在 Linux 文件系统快速导航的高级 cd 命令
    linux 查看磁盘空间大小
    js刷新页面方法大全
    [知乎有感] 读研到底为了什么,值不值得?
    [Hadoop] 在Ubuntu系统上一步步搭建Hadoop(单机模式)
  • 原文地址:https://www.cnblogs.com/scaugsh/p/6402428.html
Copyright © 2011-2022 走看看