原题链接:https://www.acwing.com/problem/content/description/173/
思路:乍一看是背包问题,对于每一个礼物都有装或不装两种选择,又因为N在1-46,这样就会出现2^46次方,复杂度过高,采用双向dfs策略。
先将所有礼物重量从大到小排序,对于前半部分,选出若干个可能达到的0-W之间的所以重量存放在数组w中。
然后将w排序并去重,再对于后半部分,选出一些,对于每个可能达到的重量t,在数组w中二分查找<=W-t的最大的一个,更新ans.
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1<<25//因为要二分,所以46的范围,此处开到25差不多
int m,n,k,a[50],w[N],cnt=0,ans;
void dfs1(int u, int s){//第一次dfs
if(u==k){
w[cnt++]=s;//存储可能值
return;
}
dfs1(u+1,s);//不选
if((ll)s+a[u]<=m){//选
dfs1(u+1,s+a[u]);
}
}
void dfs2(int u, int s){
//对于每一个s,二分查找<=m-s的最大值
if(u==n){
int l=0,r=cnt-1;
while(l<r){
int mid=(l+r+1)>>1;
if(w[mid]<=m-s){
l=mid;
}
else r=mid-1;
}
ans=max(ans,w[l]+s);//更新答案
return;
}
dfs2(u+1,s);//不选
if((ll)s+a[u]<=m){//选
dfs2(u+1,s+a[u]);
}
}
int main(){
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
reverse(a,a+n);
k=n/2+2;//前半部分
dfs1(0,0);
sort(w,w+cnt);//排序
int t=1;
for(int i=1;i<cnt;i++){
if(w[i]!=w[i-1]){// 去重
w[t++]=w[i];
}
}
cnt=t;
dfs2(k,0);
cout<<ans<<endl;
return 0;
}