【折半搜索】 洛谷 P4799 [CEOI2015 Day2]世界冰球锦标赛

博客讨论了在解决洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛问题时,普通搜索存在的时间复杂度过高的问题,并提出了使用折半搜索的思路。通过将40场比赛分为两组,分别进行常规搜索并记录花费,然后对一组排序,利用折半搜索在另一组中快速找到匹配的花费组合,从而降低时间复杂度。
摘要由CSDN通过智能技术生成

我只会看题解和抄题解

普通搜索在这道题中存在的问题

一共最多有40场比赛,每一场比赛有看和不看2种选择,如果求看40场比赛一共有多少选择,最多有2^40种可能性,时间复杂度太高。

折半搜索思路

1.把这40场比赛拆开,看成2个20场的比赛分别进行常规搜索。但是这两次常规搜索得到的所有的可能性所花费的金钱都要用数组记录下来。(数组中记录的是所有的可能性所花费的金钱,若金钱有相同的,也会重复记录下来,因为我们不是要找到所有的花费数目,而是找到所有的看比赛的可能性花费的金钱)
2.把其中一个用于记录的数组排序。
3.遍历另一个没排序的数组,假设其为s[]。s[i]表示在s[]数组涵盖的一半的场次中的第i个可能性所花费的金钱,m-s[i]表示当第i个的可能性发生时可以花费在另外一半的场次中的金钱。于是可以对排过序的数组折半查找m-s[i]这个数。找到的位置前面有多少个元素,一半场次的第i个可能性便在所有的场次中拥有几种可能性。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
typedef long long int LL;
LL s1[2000000],s2[2000000];
int n;
LL m;
LL qu[45];
int cnt1,cnt2;
LL ans;

void dfs(int l,int r,int &cnt,LL total,LL s[])
{
   if(m < total) return;
   if(l>r){
      s[++cnt] = total;
      //std::cout << "cnt=" << cnt << " s[" << cnt << "]=" << s[cnt] << std::endl;
      return ;
   }
   dfs(l+1,r,cnt,total,s);
   dfs(l+1,r,cnt,total+qu[l],s);
}

int main()
{
   scanf("%d%lld",&n,&m);
   for(int i=1; i<=n; i++){
      scanf("%lld",&qu[i]);
   }
   int mid = (1+n) >> 1;
   cnt1 = cnt2 = ans = 0;
   dfs(1,mid,cnt1,0,s1);
   dfs(mid+1,n,cnt2,0,s2);
   std::sort(s1+1,s1+cnt1+1);
   for(int i=1; i<=cnt2; i++){
      LL left = m-s2[i];
      if(left >= 0 ){
      //最开始忘了加等号,导致当s2[i]=m(金钱上限)时,不会统计在s2看m块钱,不在s1看的情况
      //其实这个if不用要,刚开始啥也不会,就瞎写的。
         int ii = std::upper_bound(s1+1,s1+1+cnt1,left)-(s1+1);
         //std::cout << "s2[" << i << "]有" << ii << "种情况" << std::endl;
         ans += ii;
      }
   }
   printf("%lld",ans);

   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值