题目描述:
达达帮翰翰给女生送礼物,翰翰一共准备了N个礼物,其中第i个礼物的重量是G[i]。
达达的力气很大,他一次可以搬动重量之和不超过W的任意多个物品。
达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
输入格式
第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i]。
输出格式
仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。
数据范围
1≤N≤46,
1≤W,G[i]≤231−1
解题思路:
我们可以先选n/2个物品,让他们自由组合把总质量不超过m的物品记录下来,然后存到s数组中,接着第二次dfs处理n/2到n的物品,然后在s数组中二分查找大于等于m-sum(sum是第二部分的物品质量)的最优解即可 (这里第一部分开到了 n/2+2 , 是一种玄学优化,速度更快,有兴趣的铁汁可以去搜搜咋证明的).
Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=(1<<25)+7;
int n,cnt;
ll m,ans,sum,a[maxn],s[maxn];
bool cmp(ll u,ll v){
return u>v;
}
void find(ll sum){
int l=1,r=cnt,index=m-sum,k=-1;
while(l<=r){
int mid=(l+r)/2;
if(s[mid]<=index){
k=mid;
l=mid+1;
}
else r=mid-1;
}
ans=max(ans,s[k]+sum);
}
void dfs1(int x,ll sum){
if(x==(n/2+2)+1){
s[++cnt]=sum;
return ;
}
dfs1(x+1,sum);
if(a[x]+sum<=m) dfs1(x+1,a[x]+sum);
}
void dfs2(int x,ll sum){
if(x==n+1){
find(sum);
return ;
}
dfs2(x+1,sum);
if(a[x]+sum<=m) dfs2(x+1,a[x]+sum);
}
int main(){
scanf("%lld%d",&m,&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n,cmp);
dfs1(1,0);
sort(s+1,s+1+cnt);
cnt=unique(s+1,s+1+cnt)-s-1;
dfs2(n/2+3,0);
printf("%lld\n",ans);
return 0;
}