2018网易计算机视觉岗实习生笔试题解

牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容量为w。
牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。
牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。

输入描述:
输入包括两行
第一行为两个正整数n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的数量和背包的容量。
第二行n个正整数v[i](0 <= v[i] <= 10^9),表示每袋零食的体积。


输出描述:
输出一个正整数, 表示牛牛一共有多少种零食放法。

输入例子1:
3 10
1 2 4

输出例子1:
8

例子说明1:
三种零食总体积小于10,于是每种零食有放入和不放入两种情况,一共有2*2*2 = 8种情况。
【题目分析】:这一类背包问题有两种方法,一种是当物品个数n较少,但是背包大小m比较大时采用指数级的枚举搜索,复杂度为O(2^n),另一种是当背包比较小的时候采用动态规划O(nm)。
这道题明显是属于第一种。但是2^30的复杂度是不可以接受的,因此我们可以采用中途相遇法。把我们能接受的前15个物品先进行第一次枚举搜索,然后再对剩下的物品进行第二次枚举搜索。把第二次枚举搜索出来的结果(至多2^15=32768个答案)存入数组并排序,枚举第一次搜出来的结果,计算出还剩下多少背包体积还能装,在第二次的结果中进行二分搜索,并把两次搜索的结果进行相乘(乘法原理)。再把所有的结果进行相加(加法原理),就是答案了。
我为了让小数据算的快一些,当n<20时直接枚举搜索出答案了。
当然也可以把物品分成n/2和n-n/2两部分。
总复杂度是O(2^(n/2)*n)。

#include 
using namespace std;
typedef long long LL;
int main()
{
    for (LL n, w; cin >> n >> w; ) {
        vector v;
        for (int i = 0, x; i < n; cin >> x, v.push_back(x), ++i) {}
        LL n1 = n >> 1, n2 = n - n1;
        vector arr;
        for (int i = 0; i < (1 << n1); i++) {
            LL sum = 0;
            for (int bits = 0; bits < n1; bits++)
                sum += (i & (1 << bits)) ? v[bits] : 0;
            arr.push_back(sum);
        }
        sort(arr.begin(), arr.end());
        LL ans = 0;
        for (int i = 0; i < (1 << n2); i++) {
            LL sum = 0;
            for (int bits = 0; bits < n2; bits++)
                sum += (i & (1 << bits)) ? v[n1 + bits] : 0;
            ans += lower_bound(arr.begin(), arr.end(), w - sum + 1) - arr.begin();
        }
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(2018实习笔试)