【搜索】【Meet in the middle】世界冰球锦标赛Ice Hockey World Championship

该博客介绍了如何利用Meet in the Middle算法优化解决CEOI2015中关于世界冰球锦标赛观赛方案的问题。在给定比赛数量、预算和每场比赛票价的情况下,寻找总票价不超过预算的不同观赛方案数。博客提供了问题描述、输入输出样例、解题思路及代码实现。

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

题目描述

译自 CEOI2015 Day2 T1「Ice Hockey World Championship

今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

输入

第一行,两个正整数 N 和 M(1≤N≤40,1≤M≤10^18),表示比赛的个数和 Bobek 那家徒四壁的财产。

第二行,N 个以空格分隔的正整数,均不超过 10^16,代表每场比赛门票的价格。

输出

输出一行,表示方案的个数。由于 N 十分大,注意:答案 ≤240。

输入样例

5 1000
100 1500 500 500 1000

输出样例

8

说明

样例解释

八种方案分别是:

  • 一场都不看,溜了溜了
  • 价格 100 的比赛
  • 第一场价格 500 的比赛
  • 第二场价格 500 的比赛
  • 价格 100 的比赛和第一场价格 500 的比赛
  • 价格 100 的比赛和第二场价格 500 的比赛
  • 两场价格 500 的比赛
  • 价格 1000 的比赛

有十组数据,每通过一组数据你可以获得 10 分。各组数据的数据范围如下表所示:

数据组号1-23-45-78-10
N≤10204040
M≤10^610^1810^610^18

分析

首先考虑暴力解法。

枚举每场比赛的状态,算出所有方案,若方案满足条件计数器累加。

观察数据范围,这样做跑下来搜索树过于庞大。

考虑使用Meet in The Middle算法进行优化(参考博客 meet in the middle

我们把所有的价格分成两部分,分别处理出方案,然后统计答案

代码

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

long long n, m;
long long p[47];
long long a[10000010], b[10000010];
long long cnta, cntb;
long long ans;

inline long long read() {
    long long x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

void dfs_a(long long l, long long r, long long sum) {
    if (sum > m) return;
    if (l > r) {
        a[++cnta] = sum;
        return;
    }
    dfs_a(l + 1, r, sum + p[l]);//选 
    dfs_a(l + 1, r, sum);//不选 
}

void dfs_b(long long l, long long r, long long sum) {
    if (sum > m) return;
    if (l > r) {
        b[++cntb] = sum;
        return;
    }
    dfs_b(l + 1, r, sum + p[l]);//选 
    dfs_b(l + 1, r, sum);//不选 
}



int main() {
    n = read(), m = read();
    for (long long i = 1; i <= n; i++) p[i] = read();
    dfs_a(1, n >> 1, 0);//分成两部分分别处理
    dfs_b((n >> 1) + 1, n, 0);
    sort(a + 1, a + cnta + 1);
    for (long long i = 1; i <= cntb; i++) ans += upper_bound(a + 1, a + cnta + 1, m - b[i]) - a - 1;
    //upper_bound(a + 1, a + cnta + 1, m - b[i])返回a数组中第一个大于等于m - b[i]的值的位置
    //因为a数组升序排序,所以upper_bound的返回值之前的所有值都满足条件
    printf("%lld", ans);
}


当然,各位大佬也可以将dfs数组写成带指针的形式,依照个人习惯而定

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值