描述
作为惩罚,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
1:01背包不行,体积过大;
2:每个礼物选或者不选,2的n次方,不行;
可行的一种方案:我们可以把礼物均分成二段,先用选或者不选前一段,会得到可以组从的体积的所有可能,当然我们取小于等于w的,假设我们用数组num记录;然后将num排序;然后我们再次dfs第二段礼物,又可以求出第二段可以组成的体积,这个我们不存,每次搜索到n时,我们有一个第二段和的体积,我们用x=w-sum;这样我们在第一段和中二分x,就达到目的;
我们来看一下复杂度 2^n/2+2^n/2*log2(2^n/2) 差不多就是n*2^(n/2);
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<cstring> using namespace std; #define ll long long typedef pair<int,int>P; const int len=50; int w,n; int l,h; int arr[len]; int num[1<<24+4]; void dfs1(int pos,ll sum) { if(pos==h) { num[l++]=sum; return ; } dfs1(pos+1,sum); if(sum+arr[pos]<=w)dfs1(pos+1,sum+arr[pos]); } ll ans; void search(ll sum) { int x=w-sum; int y=upper_bound(num,num+l,x)-num; if(y==0)ans=max(ans,sum); else ans=max(ans,num[y-1]+sum); } void dfs2(int pos,ll sum) { if(pos==n) { search(sum); return ; } dfs2(pos+1,sum); if(arr[pos]+sum<=w)dfs2(pos+1,sum+arr[pos]); } int main() { cin>>w>>n; for(int i=0;i<n;++i)scanf("%d",arr+i); sort(arr,arr+n,greater<int>()); h=n/2+1; dfs1(0,0); sort(num,num+l); l=unique(num,num+l)-num; dfs2(h,0); cout<<ans<<endl; }