TYVJ1340送礼物

描述

作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不这么认为了。某神牛有N个礼物,且异常沉重,但是 GY的力气也异常的大(-_-b),他一次可以搬动重量和在w(w<=2^31-1)以下的任意多个物品。GY希望一次搬掉尽量重的一些物品,请你 告诉他在他的力气范围内一次性能搬动的最大重量是多少。

输入格式

第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i],G[i]<= 2^31-1。

输出格式

仅一个整数,表示GY在他的力气范围内一次性能搬动的最大重量。

测试样例1

输入

20 5
7
5
4
18
1

输出

19

备注

对于20%的数据 N<=26
对于40%的数据 W<=2^26
对于100%的数据 N<=45 W<=2^31-1

思想

本题如果使用笨办法,对n个物品选或不选,复杂度为O(2^n),2^45肯定炸了

利用折半搜索的思想进行拆分双向枚举,可以使复杂度降至O(2^(n/2+1)),搜索规模基本少了一半

显然,最后将每个可能产生的值进行从小到大排序,二分查询,可以使效率更高

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <cstdio>
#include <algorithm>
using namespace std;
long long w,n;
long long g[45+6],ans;
long long num1[(1<<23)+66],num2[(1<<23)+66],cnt1,cnt2;
void dfs1(int step,long long tot){
if(tot>w)return;
if(step>(n/2)){
num1[++cnt1]=tot;
return;
}
dfs1(step+1,tot+g[step]);
dfs1(step+1,tot);
}
void dfs2(int step,long long tot){
if(tot>w)return;
if(step>n){
num2[++cnt2]=tot;
return;
}
dfs2(step+1,tot+g[step]);
dfs2(step+1,tot);
}
int main(){
scanf("%lld%lld",&w,&n);
for(int i=1;i<=n;++i)
scanf("%lld",&g[i]);
dfs1(1,0);
dfs2(n/2+1,0);
sort(num1+1,num1+cnt1+1);
sort(num2+1,num2+cnt2+1);
ans=max(num1[cnt1],num2[cnt2]);
long long l=1;
for(long long i=cnt2;i>=1;--i){
while(num2[i]+num1[l]<=w&&l<=cnt1)++l;
--l;
ans=max(ans,num2[i]+num1[l]);
}
printf("%lld",ans);
}