TYVJ 1340 算法竞赛进阶指南 送礼物 双向搜索

描述

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

输入格式

第一行两个整数,分别代表W和N。
以后N行,每行一个正整数表示G[i],G[i]<= 2^31-1。

输出格式

仅一个整数,表示GY在他的力气范围内一次性能搬动的最大重量。

样例输入

20 5
7
5
4
18
1

样例输出

19

数据范围与约定

  • 对于20%的数据 N<=26
    对于40%的数据 W<=2^26

  • 对于100%的数据 N<=45 W<=2^31-1

1:01背包不行,体积过大;

2:每个礼物选或者不选,2的n次方,不行;

可行的一种方案:我们可以把礼物均分成二段,先用选或者不选前一段,会得到可以组从的体积的所有可能,当然我们取小于等于w的,假设我们用数组num记录;然后将num排序;然后我们再次dfs第二段礼物,又可以求出第二段可以组成的体积,这个我们不存,每次搜索到n时,我们有一个第二段和的体积,我们用x=w-sum;这样我们在第一段和中二分x,就达到目的;

我们来看一下复杂度 2^n/2+2^n/2*log2(2^n/2)  差不多就是n*2^(n/2);

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
#define ll long long
typedef pair<int,int>P;
const int len=50;
int w,n;
int l,h;
int arr[len];
int num[1<<24+4];
void dfs1(int pos,ll sum)
{
    if(pos==h)
    {
        num[l++]=sum;
        return ;
    }
    dfs1(pos+1,sum);
    if(sum+arr[pos]<=w)dfs1(pos+1,sum+arr[pos]);
}
ll ans;
void search(ll sum)
{
    int x=w-sum;
    int y=upper_bound(num,num+l,x)-num;
    if(y==0)ans=max(ans,sum);
    else ans=max(ans,num[y-1]+sum);
}
void dfs2(int pos,ll sum)
{
    if(pos==n)
    {
        search(sum);
        return ;
    }
    dfs2(pos+1,sum);
    if(arr[pos]+sum<=w)dfs2(pos+1,sum+arr[pos]);
}
int main()
{
    cin>>w>>n;
    for(int i=0;i<n;++i)scanf("%d",arr+i);
    sort(arr,arr+n,greater<int>());
    h=n/2+1;
    dfs1(0,0);
    sort(num,num+l);
    l=unique(num,num+l)-num;
    dfs2(h,0);
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/hutwuguangrong/article/details/80569526