题意:给了
m
,
n
m,n
m,n,其中
m
≤
2
31
−
1
,
n
≤
45
m\le 2^{31}-1,n\le 45
m≤231−1,n≤45,给出了
n
n
n个物品,问最后在容量为
m
m
m的情况下最多能装多少东西?
题解:01背包的解法是
O
(
n
v
)
O(nv)
O(nv)的,但是
n
n
n却很小,如果直接搜索
2
45
2^{45}
245也会
T
T
T,那么可以先搜索一半,然后把这一半的重量存下来,之后再枚举另一半,然后通过二分搜索前面一半重量与它相加起来不超过
m
m
m的最大值,其中第一点,可以优化搜索顺序,将物品从大到小排序,使得搜索树变小,其次,第一个
d
f
s
dfs
dfs中就是单纯的
2
n
2
2^{\frac{n}{2}}
22n的复杂度,但是第二个
d
f
s
dfs
dfs的复杂度就是
2
n
2
∗
l
o
g
(
c
n
t
)
,
c
n
t
<
=
2
24
2^{\frac{n}{2}}*log(cnt),cnt<=2^{24}
22n∗log(cnt),cnt<=224,也就是第二个搜索多加上一个二分搜索的时间,然后我们就可以考虑将前一个
d
f
s
dfs
dfs多搜索几个数,将后一个
d
f
s
dfs
dfs少搜几个数,那么差不多两者就能均衡了。
c
o
d
e
:
code:
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1<<24;
int m,n,k,cnt,g[50],weights[N],ans;
void dfs_1(int u,int s)
{
if(u==k){
weights[cnt++]=s;
return ;
}
if((ll)s+g[u]<=m)dfs_1(u+1,s+g[u]);
dfs_1(u+1,s);
}
void dfs_2(int u,int s)
{
if(u==n){
int l=0,r=cnt-1;
while(l<r){
int mid=l+r+1>>1;
if((ll)weights[mid]+s<=m)l=mid;
else r=mid-1;
}
if((ll)weights[l]+s<=m)ans=max(ans,weights[l]+s);
return ;
}
if((ll)s+g[u]<=m)dfs_2(u+1,s+g[u]);
dfs_2(u+1,s);
}
int main()
{
m=read();n=read();
for(int i=0;i<n;i++)g[i]=read();
k=n/2+2;
sort(g,g+n);
reverse(g,g+n);
dfs_1(0,0);
sort(weights,weights+cnt);
cnt=unique(weights,weights+cnt)-weights;
dfs_2(k,0);
printf("%d",ans);
return 0;
}