【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;
}

posted @ 2022-05-24 20:40  fczhao  阅读(197)  评论(0)    收藏  举报