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