1. 程式人生 > >2018網易計算機視覺崗實習生筆試題解

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 <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
    for (LL n, w; cin >> n >> w; ) {
        vector<LL> v;
        for (int i = 0, x; i < n; cin >> x, v.push_back(x), ++i) {}
        LL n1 = n >> 1, n2 = n - n1;
        vector<LL> 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;
}