zoukankan      html  css  js  c++  java
  • 【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^{frac n2}*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
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    yapi 接口管理-格式化脚本
    如何快速将网站变为黑白?
    vue自定义事件传参
    重写vue1.X的broadcast和dispatch方法(ElementUI)
    h5 左右滑动切换tab栏
    安装pip和pylint
    使用jquery/javascript 获取网络时间
    关于手机适配的方案(transform)
    项目搭建模板
    AngularJS1.X版本双向绑定九问
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7239713.html
Copyright © 2011-2022 走看看