【bzoj4800】[Ceoi2015]Ice Hockey World Championship 折半搜索
阿新 • • 發佈:2017-07-26
ros 一個數 pan 個數 microsoft 價格 ons cpp soft
題目描述
有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 折半搜索