2018網易計算機視覺崗實習生筆試題解
阿新 • • 發佈:2019-01-02
牛牛準備參加學校組織的春遊, 出發前牛牛準備往揹包裡裝入一些零食, 牛牛的揹包容量為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; }