我只会看题解和抄题解
普通搜索在这道题中存在的问题
一共最多有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;
}