超大背包问题 (折半枚举)

题意:有重量和价值分别为wi,vi的n个物品。从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

限制条件:

1 <= n <= 40

1 <= wi, vi <= 10 的15次幂

1 <= W <= 10的15次幂

输入:

n = 4

w = {2, 1, 3, 2}

v = {3, 2, 4, 2}

W = 5

输出:

7(挑选0、1、3号物品)

分析:

这个问题是前面介绍过的背包问题,不过这次价值和重量都可以是非常大的数值,相比之下n比较小。使用DP求解背包问题的复杂度是O(nW),因此不能用来解决这里的问题。

我们可以向之前的题一样拆成两半之后再枚举,因为每部分只有20个,所以是可行的。利用拆成两半后的两部分的价值和重量,我们能求出原先的问题。

我们把前半部分中的选取方法对应的重量和价值总和记为w1, v1。这样在后半部分寻找总重w2 <= W - w1时使v2最大的选取方法就好了。

因此,我们要思考从枚举得到的(w2, v2)的集合中高效寻找max{v2|w2<= W‘}的方法。首先,显然我们可以排除所有w2[i] <= w2[j]并且v2[i] >= v2[j]的j。这一点可以按照w2, v2的字典序排序后简单做到。此后剩余的元素都满足w2[i] < w2[j]===v2[i] < v2[j],要计算max{v2|w2<= W‘}的话,只要寻找满足w2[i] <= W‘的最大的i就可以了。这可以用二分搜索完成。

#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 40 + 5;
const int INF = 10000000;

int n;
ll w[maxn], v[maxn];
ll W;

pair<ll, ll> ps[1 << (maxn / 2)];    //(重量, 价值)
void solve()
{
    //枚举前半部分
    int n2 = n / 2;
    for (int i = 0; i < 1 << n2; i++){
        ll sw = 0, sv = 0;
        for (int j = 0; j < n2; j++){
            if (i >> j & 1){
                sw += w[i];
                sv += v[i];
            }
        }
        ps[i] = make_pair(sw, sv);
    }

    //去除多余的元素
    sort(ps, ps + (1 << n2));
    int m = 1;
    for (int i = 1; i < 1 << n2; i++){
        if (ps[m - 1].second < ps[i].second){
            ps[m++] = ps[i];
        }
    }

    //枚举后半部分并求解
    ll res = 0;
    for (int i = 0; i < 1 << (n - n2); i++){
        ll sw = 0, sv = 0;
        for (int j = 0; j < n - n2; j++){
            if (i >> j & 1){
                sw += w[n2 + j];
                sv += v[n2 + j];
            }
        }
        if (sw <= W){
            ll tv = (lower_bound(ps, ps + m, make_pair(W - sw, INF)) - 1) -> second;
            res = max(res, sv + tv);
        }
    }
    printf("%lld\n", res);
}
时间: 2024-11-06 19:45:32

超大背包问题 (折半枚举)的相关文章

超大背包问题(折半枚举, 双向搜索)

有重量和价值分别为wi, vi (1 <= wi, vi <= 1e15)的n (1 <= n <= 40)个物品,从中挑选总重不超过W(1 <= W <= 1e15)的物品,求价值总和最大值. 这是典型的01背包问题,不过dp求解复杂度为O(nW),这里W太大了,因此无法求解.挑选物品方法共有2^n种,也无法直接枚举.但是拆成两半再枚举的话还是可行的,每部分最多只有20个.假设第一部分某个选取方法对应的重量和价值为w1, v1,那么只要在第二部分中寻找w2+w1<

CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。

1514: Packs Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 61  Solved: 4[Submit][Status][Web Board] Description Give you n packs, each of it has a value v and a weight w. Now you should find some packs, and the total of these value is max, total of

NYOJ 1091 超大01背包(折半枚举)

这道题乍一看是普通的01背包,最最基础的,但是仔细一看数据,发现普通的根本没法做,仔细观察数组发现n比较小,利用这个特点将它划分为前半部分和后半部分这样就好了,当时在网上找题解,找不到,后来在挑战程序设计上找到了这个题,就拿来引用一下 挑选物品的方法总从2^n中,直接枚举肯定不行,因为n最大为40,但是如果n为20就可以了,这时候就要用到折半枚举,先枚举前一半,在枚举后一半.先把前把部分的选取方法对应的重量和价值总和记为w1, v1,这样后半部分寻找w2 <= W - w1时 使v2最大的选取方

np背包问题【算法:折半枚举】

先看题吧 我猜到多数人开始想的就是二进制暴力枚举,对吧,这题这样做可以因为只需要提交答案,完全可以本地暴力,1e9的数据.需要一分钟左右. 代码: 这里我就不说着种方法了. 这题我们可以用,折半枚举. 优点:就是能够将时间复杂度降下来,将2^30 变为 2^15 + 2^15 解决这题仅仅用了不到1s的时间 代码也 相当于模板 1 #include <algorithm> 2 #include <map> 3 #include <string> 4 #include &

poj 2785 4 Values whose Sum is 0 折半枚举

题目链接:http://poj.org/problem?id=2785 枚举的一般思路就是先把所有的状态枚举出来 最后一次性判断该状态合法不合法 而折半枚举的思想是 先枚举一半的状态 把他们的状态存起来 排序 然后再枚举剩下一般 用目标反推前一半的期望状态 接下来在前一半的结果数组中查找是否有相应结果 之所以能优化是因为结果数组有序 就可以用二分搜索 复杂度从O(n^2 * n^2) 降到 O(n^2 * log(n^2))即(O(n^2 * log n)) 二分搜索的一个技巧 在有序数组中用二

超大背包问题(01背包)

超大背包问题:有n个重量和价值分别为w[i]和v[i]的物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值.其中,1 ≤ n ≤ 40, 1 ≤ w[i], v[i] ≤ 10^15, 1 ≤ W ≤ 10^15. 这个问题给人的第一感觉就是普通的01背包.不过,看完数据范围会发现,这次价值和重量都可以是非常大的数值,相比之下n比较小.使用DP求解背包为题的复杂度是O(nW),因此不能用来解决这个问题.此时我们应该利用n比较小的特点来寻找其他方法. 挑选物品的方案总共有

Load Balancing 折半枚举大法好啊

Load Balancing 给出每个学生的学分.   将学生按学分分成四组,使得sigma (sumi-n/4)最小.         算法:   折半枚举 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <string> 7 #include <

lightoj 1235 Coin Change (IV)(折半枚举)

话说这是俺们学校暑假集训完的一道题,刚看到以为是到水题,后来发现者复杂度也太大了,受不了了,比赛完也没搞出来,然后欣爷说这是折半枚举.然后就摸摸的学了一下,又把这道题写了一下, 所谓折半枚举就是先算出来一半,再算一半,然后用二分查找看看能不能搞到这一发状态,可以的话就是可以了, 题意:给你两个数n,k,下面再给你n个数,表示你现在有的硬币的面值,每种硬币面值的有两个,看是否可以支付k 题解思路:首先以为只有三种状态,直接dfs就好了,后来发现复杂度太大了,然后死了撸就是上面的,详细情残考代码 源

HDU 5340 Three Palindromes( 折半枚举+Manacher+记录区间 )

Three Palindromes Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 809    Accepted Submission(s): 240 Problem Description Can we divided a given string S into three nonempty palindromes? Input F

Eqs 折半枚举+二分查找 大水题

Eqs 题目抽象:a1x13+ a2x23+ a3x33+ a4x43+ a5x53=0 (*),给出a1,a2,a3,a4,a5.    ai属于[-50,50]. 求有多少序列   x1,x2,x3,x4,x5 ,xi属于 [-50,50]-{0}. 思路:折半枚举+二分查找 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #inclu