NYOJ 还是01背包(枚举+二分)

来源:互联网 发布:淘宝可用微信付款吗 编辑:程序博客网 时间:2024/06/05 03:01
还是01背包
时间限制:10000 ms | 内存限制:228000 KB
难度:5
描述
有n个重量和价值分别为 wi 和 vi 的物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

输入
多组测试数据。
每组测试数据第一行输入n 和 W ,接下来有n行,每行输入两个数,代表第i个物品的wi 和 vi。
1 <= n <=40
1 <= wi <= 10^15
1 <= vi <= 10^15
1 <= W <= 10^15
输出
每组数据输出一行,代表挑选方案中价值总和的最大值。
样例输入
4 5
2 3
1 2
3 4
2 2
样例输出

7


这题写了好久,调了好久,心塞。

这题解题思路是这样的,首先看一下vi, wi, W便知太大了,所有的背包动态规划方程都显得无能为力了。看一下n<=40, 没办法了只能从这边入手了。怎么做呢?没想到其他的,直接枚举就是了。但是枚举也是需要技巧的,40个得有2^40的量,会崩溃的额,也就是说时间复杂度为O(2^n);接下来就是要做优化工作了。其实以前做题的时候也做过类似思想的题,就是把n个物品分成两半,每一半就剩下n/2个,这样复杂度降的很多。用一个整数来表示方案,用到位运算来表示,例如2的二进制位11,这表示第一个和第二个都拿,又如1的二进制数为01, 这表示第一个拿,第二个物品不拿,这样表示简洁简单且速度快。设前半部分某种方案是得到的重量为w1, 价值为v1,这在第二部分中w2,v2必定有w2+w1<=W; 所以就枚举第一部分,再去第二部分中找w2<=W-w1时价值的最大值。然后第二部分开始时就应该按wi,,vi的字典序排序,因为如果有wi<wj,vi>vj那么i方案一定优于j方案。所以在第二部分中排完序之后会剔除掉某些方案。然后既然排好序就得按二分思想来查找。


AC代码:


# include <cstdio># include <algorithm>using namespace std;typedef long long int ll;struct goods{ll w;ll v;};goods s1[30], s2[30];//输入数据 goods change_s2[(1<<20)+10];//转化为w与v的和 int compare(goods a, goods b){//sort中的比较函数 if(a.w!=b.w){return a.w<b.w;}return a.v<b.v;}int main(){int n, i, v1, v2, cur1, cur2, part1, part2, _cur1, _cur2, left, right, mid;ll sum_w, sum_v, W, num_set, temp, now_w, now_v, w2, ans;while(scanf("%d%lld", &n, &W)!=EOF){part1=n/2;part2=n-part1;for(i=1; i<=part1; i++){scanf("%lld%lld", &s1[i].w, &s1[i].v);}for(i=1; i<=part2; i++){scanf("%lld%lld", &s2[i].w, &s2[i].v);}cur2=1;//枚举第二部分中的所有结果,并存储起来 ,存储的//是每种方案中w和v的和 for(num_set=0; num_set<=(1<<part2)-1; num_set++){ temp=num_set;sum_w=0;sum_v=0;for(i=1; i<=part2; i++){if(temp&1){sum_w=sum_w+s2[i].w;sum_v=sum_v+s2[i].v;}temp=temp>>1;}change_s2[cur2].w=sum_w;change_s2[cur2].v=sum_v;cur2++;}//排序 sort(change_s2+1, change_s2+cur2, compare);now_v=-1;_cur2=1;//剔除掉一些不符合的方案 for(i=1; i<=cur2-1; i++){if(change_s2[i].v>now_v){change_s2[_cur2].v=change_s2[i].v;change_s2[_cur2].w=change_s2[i].w;_cur2++;now_v=change_s2[i].v;}}ans=0;//枚举第一部分的所有情况并到第二部分二分搜索 for(num_set=0; num_set<=(1<<part1)-1; num_set++){sum_w=0;sum_v=0;temp=num_set;for(i=1; i<=part1; i++){if(temp&1){sum_w=sum_w+s1[i].w;sum_v=sum_v+s1[i].v;}temp=temp>>1; }if(sum_w<=W){w2=W-sum_w;left=1;right=_cur2-1;//二分搜索 while(left<=right){mid=(left+right)/2;if(change_s2[mid].w<=w2){left=mid+1;if(ans<sum_v+change_s2[mid].v){ans=sum_v+change_s2[mid].v;}}else{right=mid-1;}}}}printf("%lld\n", ans);}return 0;}


1 0
原创粉丝点击