描述
作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不这么认为了。某神牛有N个礼物,且异常沉重,但是GY的力气也异常的大(-_-b),他一次可以搬动重量和在w(w<=2^31-1)以下的任意多个物品。GY希望一次搬掉尽量重的一些物品,请你告诉他在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i],G[i]<= 2^31-1。
输出格式
仅一个整数,表示GY在他的力气范围内一次性能搬动的最大重量。
样例输入
20 5 7 5 4 18 1
样例输出
19
数据范围与约定
- 对于20%的数据 N<=26
对于40%的数据 W<=2^26
对于100%的数据 N<=45 W<=2^31-1
思路: 这个题其实如果W小一点就是个简单的01背包,但是这个W到了2e9,空间太大没法dp
那么就可以考虑枚举,也就是搜索,dfs的复杂度时2n,也就是说最高到245,这也很大了,但是我们可以将所有物品分成两份,分别枚举每份中可以组成的重量,然后对于其中一份的中每个重量t,在另一份中二分查找W-t,保存最大值。
这就说双向dfs了,也就是让复杂度变成了 O(N*2n/2)
另附lower_bound注释,如果数组是从小到大排序,使用lower_bound,就是在数组中查到最小的大于等于num的数:
如果数组是从大到小排序,lower_bound(ans+1,ans+1+n,num,greater<int>()) 要在其中加入greater<int>(),查找最大的小于等于num的数

#include<bits/stdc++.h> using namespace std; int W,N; int weight[55]; int ans1[9000000]; int ans2[9000000]; int cnt; void dfs(int now,int lim,int ans[],long long val) { if(val > W)return; if(now > lim) { ans[++cnt] = val; return; } dfs(now+1,lim,ans,val); dfs(now+1,lim,ans,val+weight[now]); } int main() { scanf("%d%d",&W,&N); for(int i=1;i<=N;i++) { scanf("%d",&weight[i]); } sort(weight+1,weight+1+N); reverse(weight+1,weight+1+N); int mid = (N+1)>>1; cnt = 0; dfs(1,mid,ans1,0); int cnt1 = cnt; cnt = 0; dfs(mid+1,N,ans2,0); int cnt2 = cnt; sort(ans1+1,ans1+1+cnt1); sort(ans2+1,ans2+1+cnt2); cnt1 = unique(ans1+1,ans1+1+cnt1) - ans1-1; cnt2 = unique(ans2+1,ans2+1+cnt2) - ans2-1; reverse(ans2+1,ans2+1+cnt2); int ans = 0; for(int i=1;i<=cnt1;i++) { if(ans1[i] > W)break; int pos = lower_bound(ans2+1,ans2+1+cnt2,W-ans1[i],greater<int>()) - ans2; ans = max(ans,ans1[i] + ans2[pos]); } printf("%d ",ans); }