P4799 [CEOI2015 Day2] 世界冰球锦标赛

本文介绍了在给定钱数限制的情况下,如何使用折半搜索和深度优先搜索(DFS)算法来寻找最优解决方案。通过分两步搜索并结合排序和上界查找,降低了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Portal.

折半搜索(meet in the middle)。

首先考虑正常的搜索,时间复杂度 O ( 2 40 ) O(2^{40}) O(240)

所以考虑折半搜索,从两边对着搜,具体就是搜索 1 ∼ n 2 1\sim\dfrac{n}{2} 12n n 2 + 1 ∼ n \dfrac{n}{2}+1\sim n 2n+1n 两个区间内的最优解。这样优化之后,时间复杂度就变为 O ( 2 n 2 + 1 ) O(2^{\frac{n}{2}+1}) O(22n+1)

第一遍搜索前半部分时,我们的限制是不超过 M M M。可以记录此时对于一个合法方案,它的消耗钱数 sum 是多少。

到了第二遍搜索后半部分时,需要知道的是,我们可以统计的方案是前后加一起小于 M M M 的方案。于是乎我们先对第一次得到的方案进行 sort,枚举选后一半的所有情况,如果和前一半合并之后总钱数仍小于 M M M,可以把这些可能全部累计到 ans 中。由于前一半的方案已经被我们排成有序的了,所以我们可以用 upper_bound 函数快速查找出可以统计的情况。

具体代码实现:

#include <bits/stdc++.h>
using namespace std;
#define int long long

// 对于此题的两次 DFS 来说,要统计 1 个 sum(当前消耗钱数)和 pos(已经搜到哪里了)

const int maxn=1<<25;//左移 25 位相当于 2 的 25 次方
int q1[maxn],q2[maxn],co[45],ans,N,M,cnt1,cnt2,mid;

void dfs1(int sum,int pos)
{
    if(sum>M) return;
    if(pos>mid) {q1[++cnt1]=sum;return;}
    dfs1(sum+co[pos],pos+1);
    dfs1(sum,pos+1);//考虑不选当前位,继续向下搜
}

void dfs2(int sum,int pos)
{
    if(sum>M) return;
    if(pos>N) {q2[++cnt2]=sum;return;}
    dfs2(sum+co[pos],pos+1);
    dfs2(sum,pos+1);
}

signed main()
{
    cin>>N>>M;
    mid=(N+1)/2;
    for(int i=1;i<=N;i++) cin>>co[i];
    dfs1(0,1);
    sort(q1+1,q1+cnt1+1);
    dfs2(0,mid+1);
    for(int i=1;i<=cnt2;i++)
    	ans+=upper_bound(q1+1,q1+cnt1+1,M-q2[i])-q1-1;
    // cout<<cnt<<endl;
    // cout<<N/2<<endl;
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值