CH2401 送礼物(双向搜索)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/82718789

题目

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

题解

双向搜索
如果从一个方向搜索,会不断的产生分支,搜索树呈△,在深层子树上浪费的时间相当多。
如果可以从两个方向同时开始搜索,那么每次搜索的任务只有一半,搜索树呈◇。
双向搜索把两棵搜索树合并在一起,产生两棵深度减半的搜索树,在中间交汇出现答案。因为这样的可以搜索覆盖整个状态空间,故答案的正确性很有保障。

双向搜索需要题目给出明确的“初态”“终态”,这题完全符合。
第一次搜索枚举用1~n/2号的物品能产生的重量。第二次枚举用n/2+1~n号物品能产生的重量。在中间交汇的地方,用过寻找前驱的方法,使得搜索2得到的重量+搜索1中的状态 最接近w。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=60;

int w,n,ans=0;
int a[maxn];

bool cmp(int a1,int a2)
{
    return a2>a1;
}

int m=0,b[(1<<23)+10];
long long now=0;
void dfs_1(int k,int ed)//1~mid
{
    if(now>w) return ;
    if(k>ed)
    {
        b[++m]=now;
        return ;
    }
    dfs_1(k+1,ed);
    
    now+=a[k];
    dfs_1(k+1,ed);
    now-=a[k];
}

void dfs_2(int k,int ed)
{
    if(now>w) return ;
    if(k>ed)//找前驱 
    {
        if(w-now<b[1]) return ;
        int tmp=upper_bound(b+1,b+m+1,w-now)-b-1;
        tmp=now+b[tmp];
        if(tmp>ans) ans=tmp;
        return ;
    }
    dfs_2(k+1,ed);
    
    now+=a[k];
    dfs_2(k+1,ed);
    now-=a[k];
}

int main()
{
    scanf("%d%d",&w,&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1,cmp);
    dfs_1(1,n/2);
    sort(b+1,b+m+1);
    dfs_2(n/2+1,n);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/82718789