2021蓝桥杯C/C++ B组 杨辉三角的二分查找法

前言
一、杨辉三角是什么?
二、二分查找?
三、题目分析
1.方法
2.代码

前言

题目描述

在这里插入图片描述

前言

2021年蓝桥杯C/C++ B组考的这个题,我当时做的时候,头真的很大,感觉栓Q了,但仔细想来这道题也不是很难,按当时我做的时候,只会暴力破解,现在方法很多了。


一、杨辉三角是什么?

杨辉三角,也称为帕斯卡三角形,是一种数学排列方式,在三角形阵列中展示二项式系数。它以中国南宋数学家杨辉命名,他在1261年的著作《详解九章算法》中介绍了这个概念。杨辉三角的每一行代表二项式展开的系数。
杨辉三角的构造是这样的:
每一行的首尾都是1,其余每个数是它正上方的两个数之和。
例如,第三行是1, 2, 1,因为 (1+1=2)。
这个性质也使得杨辉三角与组合数学紧密相关,因为每个数实际上是一个组合数,即从一组元素中选择若干元素的方法数。
更奇妙的是:杨辉三角的特性。它不仅展示了组合数学的基本原理,还揭示了许多数学性质和模式,如斐波那契数列、二项式定理、甚至概率论中的分布模式。例如,如果你沿着三角形的斜边相加,你会得到斐波那契数列。而且,杨辉三角中的数字可以用来计算概率,比如掷硬币的不同结果的概率。

二、二分查找法

二分查找法,也称为折半查找法,是一种在有序数组中查找特定元素的高效算法。它的工作原理是将查找区间分成两半,然后根据中间元素与目标值的比较结果来确定下一步搜索的区间是左半部分还是右半部分。这个过程会一直重复,直到找到目标值或者搜索区间为空。
二分查找法的基本步骤如下:
确定查找区间的初始左边界(low)和右边界(high)。
1、计算中间位置的索引(mid),通常是 (low + high) / 2。
2、比较中间位置的元素与目标值:
3、如果中间位置的元素等于目标值,则查找成功。
4、如果中间位置的元素大于目标值,则将右边界移动到 mid - 1。
5、如果中间位置的元素小于目标值,则将左边界移动到 mid + 1。
6、重复步骤2和3,直到找到目标值或者 low 超过 high(此时查找失败)。

⭐️⭐️⭐️⭐️⭐️

注意事项
1、有序数组:二分查找法适用于有序数组。如果数组是无序的,需要先进行排序。
2、边界条件:正确处理查找区间的边界条件是二分查找法中的关键。通常,左边界初始化为0,右边界初始化为数组长度减1。
3、中间点计算:计算中间点时,推荐使用 mid = left + (right - left) / 2 而不是 (left + right) / 2,以避免潜在的整数溢出问题。
4、循环或递归终止条件:确保循环或递归的终止条件正确,以防止无限循环。例如,当 left 大于 right 时,循环应该终止。
5、更新边界:在每次迭代中,根据中间元素与目标值的比较结果,正确更新左边界或右边界。
返回值:如果找到目标值,返回其索引;如果没有找到,确保返回一个表示未找到的值,如 -1。

⭐️⭐️⭐️⭐️⭐️

三、题目分析:

(1)

通过给出的评测用例可知,时间复杂度为 10^9,我们无法通过暴力来解决这道题,所以我们来好好分析一下:
一部分实例图
⚠️斜行和横行(横行每一个数)都是从0开始的
根据我图中所画,我们只需要求解左半部分就行。我们在观察图片时发现每一斜行(除第一斜行)都是有序递增的,再观察每一横行,会发现越靠近中间数越大,那么可知,
1、 越往下面的斜行的起始数就越大,因此,第一斜行起始数C(0,0)=1,第二斜行起始数C(2,1)=2,第三斜行起始数C(4,2)=6,第四斜行起始,C(6,3)=20。
2、 第i斜行的起始数为C(2n,n), ni 的关系为 n=i-1
3、 我们知道该题目的时间复杂度为10^9 , 那 C(16,32) < 10^9, 🥚C(17,34) 会> 10^9, 于是我们就从第16斜行开始枚举,🥚我们应枚举到小于查找的数,大于下一斜行的起始数,然后找到这一斜行的起始数,在这一斜行中开始二分查找。
4、 查找此数要用到等差公式:n*(n+1)/2,然后再加上 k+1

公式:  n * ( n + 1 ) / 2 + k + 1 

(2) 代码

// C的代码
#include <stdio.h>
#include <stdlib.h>

// 定义全局变量n,用于存储输入的目标值
int n;

// 比较函数,用来取得两个整数中的最大值,用来比较取边界的
int max(int a, int b) { 
    if (a > b) return a;
    else 
    return b;
}

// 求组合数C(a, b),即从a个不同元素中选择b个元素的方法数
int fact(int a, int b) { 
    int x = 1; // 初始化组合数结果为1
    for (int i = a, j = 1; j <= b; i--, j++) {
        x = x * i / j; // 根据组合数公式计算
        if (x > n) return x; // 如果组合数大于n,提前返回结果
    }
    return x; // 返回最终的组合数结果
}

// 查找函数,用于找到满足条件的最小r值
int find(int k) {
    int l = 2 * k, h = max(l, n); // 初始化左右边界
    long long c; // 用于存储计算结果
    while (l < h) {
        int mid = (l + h) / 2; // 计算中间值
        if (fact(mid, k) >= n) h = mid; // 如果中间值的组合数大于等于n,更新右边界
        else l = mid + 1; // 否则,更新左边界
    }
    if (fact(h, k) != n) {
        return 0; // 如果没有找到满足条件的r,返回0
    } else {
        c = (h * (h + 1)) / 2 + k + 1; // 计算满足条件的r的位置
        printf("%lld", c); // 打印结果
        return 1; // 返回1表示成功找到
    }
}

// 主函数
signed main() {
    scanf("%lld", &n); // 读取输入的目标值n
    for (int i = 16; ; i--) { // 从16开始递减搜索
        if (find(i)) break; // 如果找到满足条件的i,终止循环
    }
}

总结

代码是为了解决:在杨辉三角形中找到第一次出现给定正整数 N 的位置。这个问题我们就可以转化为寻找最小的 r,使得组合数 ( C(r, k) ) 大于或等于 N。❤️❤️❤️ 自己先琢磨透题目的意思。

  • 45
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值