【蓝桥杯每日一题】二分算法

🍎 博客主页:🌙@披星戴月的贾维斯
🍎 欢迎关注:👍点赞🍃收藏🔥留言
🍇系列专栏:🌙 蓝桥杯
🌙我与杀戮之中绽放,亦如黎明的花朵🌙
🍉一起加油,去追寻、去成为更好的自己!

蓝桥杯倒计时 43天

提示:以下是本篇文章正文内容,下面案例可供参考


🍎、二分

🍉、二分的简单定义

二分法(Bisection method) 即一分为二的方法. 设[a,b]为R的闭区间. 逐次二分法就是造出如下的区间序列([an,bn]):a0=a,b0=b,且对任一自然数n,[an+1,bn+1]或者等于[an,cn],或者等于[cn,bn],其中cn表示[an,bn]的中点.

🍉、二分的基本逻辑

算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。
基本思想:假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,
如果当前位置arr[k]值等于key,则查找成功;
若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1];
若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high],
直到找到为止,时间复杂度:O(log(n)) 。(来源百度百科)

🍉、二分的算法模板(y总)

//查找左边界 SearchLeft 简写SL
int SL(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid; 
        else l = mid + 1; 
    }   
    return l;
}
//查找右边界 SearchRight 简写SR 
int SR(int l, int r) 
{
    while (l < r)
    {                   
        int mid = l + r + 1 >> 1; //需要+1 防止死循环
        if (check(mid)) l = mid;
        else r = mid - 1; 
    }
    return r;
}

🔥博主对于在做题时该选择哪种模板时的看法:

    首先我们在考虑使用哪个模板时,其实就是考虑mid, R,和 L的取值,我们肯定要先分析题目的意思,如果我们的答案在mid的右边,那么我们优先使用枚举区间右端点的模板,也就是选择mid = l + r + 1 >> 1往上取整的模板,同理,我们如果要的答案在mid的左边,择选择第一种mid = l + r >> 1的模板。还有,我们在考虑这道题能不能使用二分时,其实对于这道题是否具有单调性并不看重,如果具有二段性,就能使用二分。

🔥二分的几个应用场景

1:找大于等于数的第一个位置 (满足某个条件的第一个数)
2:找小于等于数的最后一个数 (满足某个条件的最后一个数)
3.查找最大值 (满足该边界的右边界)、
4.查找最小值 (满足该边界的左边界)


🍎、例题分析

🍇、(AcWing)数的范围

本题链接: 数的范围
在这里插入图片描述
简单分析题意:先输入一个长度为n的数组,然后进行q次询问,每次询问如果这个数组存在值=x,就把这个值在x的起始位置和终止位置返回,若不存在就输出 -1 -1;

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int s[N];
int n, q;
int main ()
{
    cin >> n >> q;
    for(int i = 0; i < n; i++) cin >> s[i];
    
    while(q --)
    {
        int x;
        cin >> x;
        int l = 0, r = n - 1;
        while(l < r)//枚举左端点
        {
            int mid = l + r >> 1;
            if(s[mid] >= x) r = mid;
            else l = mid + 1;
        }
        if(s[r] == x)
        {
            cout << r << " ";
            l = 0, r = n - 1;
            while(l < r)//z枚举右端点
            {
                int mid = l + r + 1 >> 1;//此时mid在右区间, mid要向上取整,所以要+1
                if(s[mid] <= x) l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
        else cout << "-1 -1" << endl;
        
    }
    return 0;
}
🍇、(AcWing)四平方和

本题链接: 四平方和
在这里插入图片描述
简单分析题意: 由于 5 * 10^6的数据范围,所以不能枚举四个数,只能枚举两个数,把时间复杂度降到nlogn左右。
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5000010;
int n;

int h[N], m[N];
int main ()
{
    cin >> n;
    memset(h, -1,sizeof h ); //把h数组内的值全初始化为-1,标记为未用过
    for(int c = 0; c * c <= n; c++)
        for(int d = c; d * d + c * c <= n; d++)
        {
            int s = c * c + d * d;
            if(h[s] == -1)
                h[s] = c, m[s] = d;
        }
    
    for(int a = 0; a * a <= n; a++)
        for(int b = a; b * b + a * a <= n; b++)
        {
            int s = n - a * a - b * b;
            if(h[s] != -1)
            {
                printf("%d %d %d %d\n",a , b, h[s], m[s]);
                return 0;
            }
        }
    return 0;
}
🍇、(AcWing)分巧克力

本题链接: 分巧克力
在这里插入图片描述
解题思路:
在这里插入图片描述
代码示例:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, k;
int h[N], w[N];//横竖边长
bool cheak(int mid)
{
    LL res = 0;
    for(int i = 0; i < n; i++)
    {
        res += (LL)h[i]/mid *(w[i] / mid);
        if(res >= k) return true;
    }
    return false;
}
int main ()
{
    cin >> n >> k;
    for(int i = 0; i < n; i++) cin >> h[i] >> w[i];
    
    int l = 1, r = 1e5;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(cheak(mid)) l = mid;
        else r = mid - 1;
    }
    cout << r << endl;
    return 0;
}
🍇、(AcWing)我在哪?

本题链接: 我在哪?
在这里插入图片描述
简单分析题意:本道题的题意还是比较难理解的,题目又长,核心就是这一句:例如,假设沿路的邮箱序列为 ABCDABC 。
约翰不能令 K=3,因为如果他看到了 ABC,则沿路有两个这一连续颜色序列可能所在的位置。
最小可行的 K 的值为 K=4,因为如果他查看任意连续 4 个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。
本意等价于:在一个连续的字符串中找到最短的能判断是在这串字符串中唯一出现,这个字符串的长度就是k值。

解题思路:因为这道题的数据量只有100,所以可以直接暴力,也可以二分最小的mid值,两种做法。

暴力代码示例:

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

int n;
string str;
int main ()
{
    cin >> n >> str;
    for(int k = 1; k <= n; k++)
    {
        bool flag = false; //判断两个串是不是相同
        for(int i = 0; i + k - 1 <= n; i++)//i + k - 1是这个串的长度
        {
            for(int j = i + 1; j + k -1 <= n; j++)//j的枚举要从i + 1开始
            {
                bool Same = true;//判断两个串是不是相同
                for(int u = 0; u < k; u++)
                    if(str[i + u] != str[j + u])
                    {
                        Same = false;
                        break;
                    }
                    if(Same) 
                    {
                        flag = true;
                        break;
                    }
            }

        }
        if(!flag) 
        {
            cout << k << endl;
            break;
        }
    }


    return 0;
}

二分代码示例:

#include<iostream>
#include<algorithm>
#include<string>
#include<unordered_set>
using namespace std;

int n;
string str;
bool cheak(int mid)
{
    unordered_set<string> hash;
    for(int i = 0; i +  mid  -1 <= n; i++)
    {
        string s = str.substr(i, mid);
        if(hash.count(s)) return false; //如果s已经在哈希表中存在过了,返回false
        hash.insert(s);//哈希表中再插入s
    }
    return true;
}
int main ()
{
    cin >> n >> str;
    int l = 1, r = n;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(cheak(mid)) r = mid;
        else l = mid + 1;
    }

    cout << r << endl;
    return 0;
}

🍎、总结

    本文简要介绍了二分的简要概念和应用场景和经典的二分模板和几道二分的经典例题,希望大家读后能有所收获!

  • 49
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 42
    评论
### 回答1: 答案: def binary_search(arr, target): left = 0 right = len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1 ### 回答2: 二分算法也被称为折半查找算法,它是一种高效的查找算法。下面是一个用Python编写的二分算法: ```python def binary_search(arr, target): low = 0 high = len(arr) - 1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid elif arr[mid] < target: low = mid + 1 else: high = mid - 1 return -1 ``` 这个二分算法接受一个已排序的整数数组arr和一个目标值target作为输入,并返回目标值在数组中的索引,如果目标值不存在于数组中,则返回-1。 该算法首先初始化两个针low和high分别向数组的最低和最高索引。在每次循环中,它计算中间索引mid,如果中间索引处的值等于目标值,则返回中间索引。如果中间索引处的值小于目标值,则将low针移动到mid + 1的位置,否则将high针移动到mid - 1的位置。这样,算法会在数组中不断缩小范围,直到找到目标值或者找不到目标值为止。 这个二分算法的时间复杂度为O(log n),其中n是数组的大小。由于每次迭代都将搜索空间减半,所以算法的效率非常高。 ### 回答3: 二分算法,也称为折半查找算法,是一种常用的查找技术。它通过将已排好序的元素集合分成两半并递归地对其进行查找,直到找到目标元素或确定目标元素不存在。 以下是一个使用Python实现的简单二分算法: ```python def binary_search(arr, target): left = 0 right = len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1 # 测试 arr = [1, 3, 5, 7, 9, 11, 13] target = 7 result = binary_search(arr, target) if result != -1: print("元素在索引位置", result) else: print("元素不存在") ``` 在上面的代码中,binary_search函数接受一个已排好序的数组arr和目标元素target作为参数。它使用了两个变量left和right来追踪查找范围的左右边界。在每次迭代中,算法会将中间索引mid计算为left和right的平均值,并将其与目标元素进行比较。如果arr[mid]等于target,表示目标元素已经找到;如果arr[mid]小于target,则目标元素位于mid右侧,更新left为mid + 1;如果arr[mid]大于target,则目标元素位于mid左侧,更新right为mid - 1。如果迭代结束时仍未找到目标元素,函数返回-1表示目标元素不存在。 在测试部分,我们创建了一个要查找的数组arr和目标元素target,并调用binary_search函数来查找目标元素。如果找到了目标元素,则打印其索引位置;否则打印"元素不存在"。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

披星戴月的贾维斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值