题意:
有重和价值分别为wi,vi的n个物品。从这些物品中挑选出总质量不超过W的物品,求所有挑选出的方案中价值总量的最大值。
限制条件:1<=n<=40
1<=wi,vi<=10^15
1<=W<=10^15
思路:
此题如果用动态规划求解复杂度为O(nW),故不划算。
可以考虑折半搜索的方法,将所有的n个物品划分成数量对等的两部分,先穷举第一部分的每一种选取物品的情况,记录好每一种情况下的物品的重量w1和价值v1,之后进行挑选,挑选出性价比高的组合,
即如果选取两种情况有如下比较:w1[i]<=w1[j]且v1[i]>=v1[j],那说明情况i的组合总重量小并且价值又高,完全优于j,就可以把情况j给排除。
这样一来第一部分的所有情况经过筛选排序就会满足w1[i]<w1[j]且v1[i]<v1[j].
接下来再对第二部分进行枚举,对于第二部分枚举出来的每一种情况,都可以计算出相应的W-w2,其意义就是在第二部分选定了相应物品的情况下,可以继续装入的物品质量还剩下W-w2,那么剩下的这些质量要用第一部分尽可能
多的物品来填充,这时可以进行二分查找,找出一种组合,使得该组合的总质量小于等于W-w2且最接近于W-w2。穷举第二部分的所有情况,最终可以得到最优的价值总和!
代码:
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N_MAX = 40 + 2; int n; ll W; ll w[N_MAX], v[N_MAX]; struct goodss { ll first, second; goodss(){} goodss(ll a,ll b):first(a),second(b){} bool operator <(const goodss&b)const { return first < b.first; } }; goodss goods[1 << (N_MAX / 2)]; int main() { while (cin>>n) { for (int i = 0; i < n; i++) cin >>w[i]; for (int i = 0; i < n; i++) cin >>v[i]; cin >> W; for (int i = 0; i < 1 << (n / 2); i++) {//对于每一种情况而言 ll w_sum = 0, v_sum = 0; for (int j = 0; j < (n / 2); j++) { if (i&(1 << j)) { w_sum += w[j];////!!!!!!!!! v_sum += v[j]; } } goods[i] = goodss(w_sum,v_sum); } //去除一些性价比低的元素 sort(goods,goods+(1<<(n/2))); int m = 1; for (int i = 1; i < (1 << (n / 2));i++) { if (goods[m - 1].second < goods[i].second) {//说明满足条件,筛选下来 goods[m++] = goods[i]; } } ll res = 0; for (int i = 0; i < 1 << (n - n / 2); i++) {//对于每一种情况得到的sum_v,都可以通过二分搜索唯一的找到一个不大于W-sum_w的max_w ll sum_v=0, sum_w = 0; for (int j = 0; j < (n - n / 2); j++) { if (i&(1 << j)) { sum_w += w[j + n / 2]; sum_v += v[j + n / 2]; } } if (sum_w <= W) {//!!已经取得的物品重量不能超过W ll max_v; if (W-sum_w >= goods[0].first) {//最多能够取入的重量太小的话,那么剩下的物品都将无法取得 goodss* tmp = lower_bound(goods, goods + m, goodss(W - sum_w, INT_MAX)); if (tmp->first > (W - sum_w)) { tmp--; } max_v = tmp->second; } else max_v = 0;//没法从剩余的物品中取得物品放入包中 res = max(res,max_v+sum_v); } } cout << res << endl; } return 0; }