背包问题的物品数少容量大的问题(折半搜索+优化搜索顺序)

题意:给了 m , n m,n m,n,其中 m ≤ 2 31 − 1 , n ≤ 45 m\le 2^{31}-1,n\le 45 m2311,n45,给出了 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} 22nlog(cnt),cnt<=224,也就是第二个搜索多加上一个二分搜索的时间,然后我们就可以考虑将前一个 d f s dfs dfs多搜索几个数,将后一个 d f s dfs dfs少搜几个数,那么差不多两者就能均衡了。
c o d e : code: code:

#include
#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;
}

你可能感兴趣的:(bfs与dfs)