【NEUOJ1962】巨大背包问题
原题链接:巨大背包
题意:将N件物品放入最大容量为W的背包中,最大的价值是多少,其中没一件物品的价值为 \(v_i\),体积为 \(w_i\)。
需要注意的是,本题除N以外的数据范围均为\([1, 10^{15}]\)
思路:巨大背包问题虽然输入背包问题,但这种题目的做法并不是dp,当我们试图用dp去做这道题时,容易发现这题\(10^{15}\)的数据范围很难进行dp。
注意到这里的N最大为40,可以采用枚举所有情况的方式解答本题。但是枚举所有情况在最坏情况下需要\(2^{40}\)的循环次数,显然会超时。
这时,我们可以将这40个物品分成两组,每组20个物品,此时\(2^{20}\)的枚举复杂度则是可以接受的。所以思路便显示在眼前了,分成两组并分别枚举后,只需要枚举其中一组并二分寻找另一组中体积小于等于\(W-w_i\)的选法中价值最大的选法并统计答案即可。
代码如下:
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node {
int v, w;
bool operator < (const node &a) const {
if(w != a.w) return w > a.w;
return v > a.v;
}
};
long long inf = 0x3f3f3f3f3f3f3f3f;
signed main(){
#ifdef FCZHAO
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
ios::sync_with_stdio(false);
int n, W; cin >> n >> W;
vector<int> v(n), w(n);
vector<node> p1, p2;
for(int i = 0; i < n; i++)
cin >> v[i] >> w[i];
//前半组物品
for(int i = 0; i < (1 << ((n + 1) / 2)); i++) {
int vv = 0, ww = 0;
for(int j = 0; j < ((n + 1) / 2); j++) {
if(i & (1 << j)) {
vv += v[j];
ww += w[j];
}
}
p1.push_back({vv, ww});
}
//后半组物品
for(int i = 0; i < (1 << (n - (n + 1) / 2)); i++) {
int vv = 0, ww = 0;
for(int j = 0; j < (n - (n + 1) / 2); j++) {
if(i & (1 << j)) {
vv += v[j + (n + 1) / 2];
ww += w[j + (n + 1) / 2];
}
}
p2.push_back({vv, ww});
}
sort(p2.begin(), p2.end(), [](node &x, node &y) {
return x.w > y.w;
});
int ans = 0;
vector<int> vv((int)p2.size() + 1);
for(int i = (int)p2.size() - 1; i >= 0; i--) {
vv[i] = max(vv[i + 1], p2[i].v);
}
for(auto &it : p1) {
int tmp = it.v;
it.v = inf, it.w = W - it.w;;
//二分查找p2中满足条件的值
auto p = lower_bound(p2.begin(), p2.end(), it);
if(p == p2.end()) continue;
else ans = max(ans, tmp + vv[p - p2.begin()]);//更新答案
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号