描述
作为惩罚,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;
}