bzoj4800[Ceoi2015]Ice Hockey World Championship 折半搜索
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4800[Ceoi2015]Ice Hockey World Championship 折半搜索相关的知识,希望对你有一定的参考价值。
题目描述
有n个物品,m块钱,给定每个物品的价格,求买物品的方案数。
输入
第一行两个数n,m代表物品数量及钱数
第二行n个数,代表每个物品的价格
n<=40,m<=10^18
输出
一行一个数表示购买的方案数
(想怎么买就怎么买,当然不买也算一种)
样例输入
5 1000
100 1500 500 500 1000
样例输出
8
题解
裸的折半搜索meet-in-the-middle
由于直接爆搜肯定会TLE,考虑把整个序列分成左右两部分,对于每部分求出它所有可以消耗钱数的方案。然后考虑左右组合怎么能够使总钱数不超过m。
考虑枚举右边序列的某钱数,那么左边的钱数要求就是不超过m-右边钱数。所以可以对左边序列排序,然后再二分查找即可知道不超过m-右边钱数的数的个数。把这个个数累加到答案中即可。
时间复杂度$O(2^{\frac n2}*\log 2^{\frac n2}=2^{\fracn2}*n)$。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; ll a[1 << 21] , b[1 << 21] , w[50] , m; int ta , tb; void dfs(int x , int n , ll now , ll *a , int &ta) { if(now > m) return; if(x > n) { a[++ta] = now; return; } dfs(x + 1 , n , now , a , ta) , dfs(x + 1 , n , now + w[x] , a , ta); } int main() { int n , i; ll ans = 0; scanf("%d%lld" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &w[i]); dfs(1 , n >> 1 , 0 , a , ta); dfs((n >> 1) + 1 , n , 0 , b , tb); sort(a + 1 , a + ta + 1); for(i = 1 ; i <= tb ; i ++ ) { if(m - b[i] >= a[ta]) ans += ta; else ans += upper_bound(a + 1 , a + ta + 1 , m - b[i]) - a - 1; } printf("%lld\n" , ans); return 0; }
以上是关于bzoj4800[Ceoi2015]Ice Hockey World Championship 折半搜索的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4800: [Ceoi2015]Ice Hockey World Championship dfs
[bzoj4800][Ceoi2015]Ice Hockey World Championship
bzoj4800[Ceoi2015]Ice Hockey World Championship 折半搜索
[Ceoi2015]Ice Hockey World Championship