2401 送礼物 (双向搜索)

描述

作为惩罚,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

思路: 这个题其实如果W小一点就是个简单的01背包,但是这个W到了2e9,空间太大没法dp

那么就可以考虑枚举,也就是搜索,dfs的复杂度时2n,也就是说最高到245,这也很大了,但是我们可以将所有物品分成两份,分别枚举每份中可以组成的重量,然后对于其中一份的中每个重量t,在另一份中二分查找W-t,保存最大值。

这就说双向dfs了,也就是让复杂度变成了 O(N*2n/2

另附lower_bound注释,如果数组是从小到大排序,使用lower_bound,就是在数组中查到最小的大于等于num的数:

          如果数组是从大到小排序,lower_bound(ans+1,ans+1+n,num,great<int>()) 要在其中加入greater<int>(),查找最大的小于等于num的数

#include<bits/stdc++.h>
using namespace std;

int W,N;
int weight[55];
int ans1[9000000];
int ans2[9000000];
int cnt;
void dfs(int now,int lim,int ans[],long long val)
{
    if(val > W)return;
    if(now > lim)
    {
        ans[++cnt] = val;
        return;
    }
    dfs(now+1,lim,ans,val);
    dfs(now+1,lim,ans,val+weight[now]);
}

int main()
{
    scanf("%d%d",&W,&N);
    for(int i=1;i<=N;i++)
    {
        scanf("%d",&weight[i]);
    }
    sort(weight+1,weight+1+N);
    reverse(weight+1,weight+1+N);
    int mid = (N+1)>>1;
    cnt = 0;
    dfs(1,mid,ans1,0);
    int cnt1 = cnt;
    cnt = 0;
    dfs(mid+1,N,ans2,0);
    int cnt2 = cnt;
    sort(ans1+1,ans1+1+cnt1);
    sort(ans2+1,ans2+1+cnt2);
    cnt1 = unique(ans1+1,ans1+1+cnt1) - ans1-1;
    cnt2 = unique(ans2+1,ans2+1+cnt2) - ans2-1;
    reverse(ans2+1,ans2+1+cnt2);
    int ans = 0;
    for(int i=1;i<=cnt1;i++)
    {
        if(ans1[i] > W)break;
        int pos = lower_bound(ans2+1,ans2+1+cnt2,W-ans1[i],greater<int>()) - ans2;
        ans = max(ans,ans1[i] + ans2[pos]);
    }
    printf("%d\n",ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/iwannabe/p/10588550.html