洛谷P4799:世界冰球锦标赛【折半搜索】

先看题:

这道题如果直接搜索的话,那肯定是不行,因为时间复杂度太高了,是o(2^n);

但是这并不代表搜索不能用,我们可以利用《三数之和》这道题的思想:

我们可以先对前1~n/2的数据进行枚举,然后记录每一个合法方案【即需要的钱数不超过M,记其中一个合法方案值为cnt】

然后再对后n/2+1~n进行枚举,然后记录每一个合法方案【记其中一个合法方案的的总值是sum】,那么我们只需要在寻找第一次遍历中cnt小于等于M-sum的个数即可;

可以看出来,这样的时间复杂度是o(2*2^(n/2)),是满足的

那么接下来就是代码了:

#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;

const int N=100;

vector<ll> fix;
int n;
ll st[N];
ll ans;
ll g;

void dfs1(ll sum,int c){//第一次搜索1~n/2
    
    if(c>=n/2){
        fix.push_back(sum);
        return;
    }
    
    dfs1(sum,c+1);
    
    if(sum+st[c]<=g){
        dfs1(sum+st[c],c+1);
    }
    
}

void dfs2(ll sum,int c){//第二次搜索n/2~n
    
    if(c>=n){//注意upper_bound对可以随机访问的数据结构的时间复杂度是o(logn)
        ans+=upper_bound(fix.begin(),fix.end(),g-sum)-fix.begin();
        return;
    }
    
    dfs2(sum,c+1);
    
    if(sum+st[c]<=g){
        dfs2(sum+st[c],c+1);
    }
    
}

int main(){
    
    cin>>n>>g;
    
    for(int i=0;i<n;i++) cin>>st[i];
    
    dfs1(0,0);
    
    sort(fix.begin(),fix.end());
    
    dfs2(0,n/2);
    
    cout<<ans;
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值