BZOJ 4800: [Ceoi2015]Ice Hockey World Championship 搜索
4800: [Ceoi2015]Ice Hockey World Championship
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 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
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); }