BZOJ 4800: [Ceoi2015]Ice Hockey World Championship 搜索

4800: [Ceoi2015]Ice Hockey World Championship

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 552  Solved: 289
[Submit][Status][Discuss]

Description

有n个物品,m块钱,给定每个物品的价格,求买物品的方案数。

Input

第一行两个数n,m代表物品数量及钱数
第二行n个数,代表每个物品的价格
n<=40,m<=10^18

Output

一行一个数表示购买的方案数
(想怎么买就怎么买,当然不买也算一种)

Sample Input

5 1000
100 1500 500 500 1000

Sample Output

8

思路:

  考虑数据范围,40的范围显然不能爆搜、直接状压。考虑Meet In The Middle 的思想,先处理出前20个产生的钱数,再处理出后20个

,这样的复杂度是 2*2^20的。对后面的每一个结果在前面的结果里二分查找/双指针。统计小于m-f2的答案即可;

代码如下

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;
int n,cnt1,cnt2;
ll m;
ll f1[1<<21],f2[1<<21];
ll a[45];
int mid;
/*int find(ll x) {
    if(x<0)return 0;
    int l=0,r=1<<20;
    while(l<r) {
        int mi = l+r >> 1;
        if(f1[mi]>x)r=mi;
        else l=mi+1;
    }
    return l;
}*/
void dfs1(int pos,ll sum) {
    if(pos==mid) {
        f1[++cnt1]=sum;
        return ;
    }
    if(sum+a[pos+1]<=m)dfs1 (pos+1,sum+a[pos+1]);
    dfs1(pos+1,sum);
}
void dfs2(int pos,ll sum) {
    if(pos==n) {
        f2[++cnt2]=sum;
        return ;
    }
    if(sum+a[pos+1]<=m)dfs2 (pos+1,sum+a[pos+1]);
    dfs2(pos+1,sum);
}
int main() {
    scanf("%d%llu",&n,&m);
    mid = n+1 >> 1;
    for(int i=1;i<=n;i++) {
        scanf("%llu",&a[i]);
    }
    dfs1(0,0);
    dfs2(mid,0);
    sort(f1+1,f1+cnt1+1);
    sort(f2+1,f2+cnt2+1);
    int j=cnt2;
    ll ans=0;
    for(int i=1;i<=cnt1;i++) {
        while(j&&f2[j]+f1[i]>m) j--;
        if(j==0) break;
        ans+=j;
    }
    printf("%llu",ans);
}

猜你喜欢

转载自www.cnblogs.com/Tobichi/p/9113138.html