有重量和价值分别为wi, vi (1 <= wi, vi <= 1e15)的n (1 <= n <= 40)个物品,从中挑选总重不超过W(1 <= W <= 1e15)的物品,求价值总和最大值。
这是典型的01背包问题,不过dp求解复杂度为O(nW),这里W太大了,因此无法求解。挑选物品方法共有2^n种,也无法直接枚举。但是拆成两半再枚举的话还是可行的,每部分最多只有20个。假设第一部分某个选取方法对应的重量和价值为w1, v1,那么只要在第二部分中寻找w2+w1<=W且v2最大的方法就行了。寻找时可以用二分查找,总时间复杂度为O(2^(n/2)n),可以接受。
#include <iostream> #include <sstream> #include <fstream> #include <string> #include <map> #include <vector> #include <list> #include <set> #include <stack> #include <queue> #include <deque> #include <algorithm> #include <functional> #include <iomanip> #include <limits> #include <new> #include <utility> #include <iterator> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <cmath> #include <ctime> using namespace std; typedef long long LL; const int maxn = 50; const LL INF = 0x3f3f3f3f; int n; LL w[maxn], v[maxn]; LL W; pair<LL, LL> ps[1<<(maxn/2)]; // (重量, 价值) void solve() { //枚举前半部分 int n2 = n / 2; for (int i = 0; i < (1<<n2); ++i) { LL sw = 0, sv = 0; for (int j = 0; j < n2; ++j) if ((i >> j) & 1) { sw += w[j]; sv += v[j]; } ps[i] = make_pair(sw, sv); } //去除多余的元素 sort(ps, ps+(1<<n2)); int m = 1; for (int i = 1; i < (1<<n2); ++i) if (ps[m-1].second < ps[i].second) ps[m++] = ps[i]; //枚举后半部分并求解 LL res = 0; for (int i = 0; i < (1<<(n-n2)); ++i) { LL sw = 0, sv = 0; for (int j = 0; j < n-n2; ++j) if ((i >> j) & 1) { sw += w[n2 + j]; sv += v[n2 + j]; } if (sw <= W) { LL tv = (lower_bound(ps, ps + m, make_pair(W - sw, INF)) - 1)->second; res = max(res, sv+tv); } } printf("%lld\n", res); } int main() { while (~scanf("%d%lld", &n, &W)) { for (int i = 0; i < n; ++i) scanf("%lld%lld", &w[i], &v[i]); solve(); } return 0; } /* Sample Input 4 5 2 3 1 2 3 4 2 2 Sample Output 7 */
版权声明:本文为博主原创文章,转载请注明出处。
原文地址:http://blog.csdn.net/god_weiyang/article/details/47701399