ACwing算法备战蓝桥杯——Day3——二分

理论知识:

二分分为整数二分与浮点数二分,其中整数二分最为常用,它们都有一套固定模板。

整数二分:

适用于大部分具有二分查找需求的问题,不一定是寻找数字

模板:

二分的前提是区间具有二段性,典型的例子就是一个单调区间(但是不止步于单调性),单调区间能被任意一个区间内的数x分成两段,第一段是小于x的区间,第二段是大于等于x的区间;通俗来讲,就是某个元素的一边是一种性质,另一边是另一种性质.最后求出的是那个边界值
bool check(int x){......};检查是否满足某些性质
第一种模板://找左边界
int Bsearsh_1(l,r){
    while(l<r){
        mid=l+r>>1;//l+r>>1等价于(l+r)/2
        if(check(a[mid])) r=mid;
        else l=mid+1;
    }
    return r;
}

第二种模板://找右边界
int Bsearch_2(l,r){
    while(l<r){
        mid=l+r+1>>1;//因为是除法是向下取整的,所以当l,r的数值取平均数,平均数可能等于l,也就是l,r相差1                          的时候。导致每次循环都会有l=mid=l,发生死循环,所以加上一个使其向上取整;
                        是否要加上一个1取决于l=mid还是r=mid;
        if(check(a[mid])) l=mid;
        else r=mid-1;
    }
    return r;
}
两个边界值之间是等于边界值的合集

浮点数二分:

常用于求解平方根等

模板:

double bsearsh_3(double l,double r){
const double eps=1e-6;//eps表示精度,取决于题目对精度的要求,一般要比题目要求的高两位精度才不会出错;
while(r-l>eps){
    double mid=(l+r)/2;
    if(check(mid)) r=mid;
    else l=mid;
}
return l;
}

今日份刷题:我在哪,数的范围,特殊排序;

例题:

例题:我在哪?

农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!

沿路有一排共 N 个农场。

不幸的是农场并没有编号,这使得约翰难以分辨他在这条路上所处的位置。

然而,每个农场都沿路设有一个彩色的邮箱,所以约翰希望能够通过查看最近的几个邮箱的颜色来唯一确定他所在的位置。

每个邮箱的颜色用 A..Z 之间的一个字母来指定,所以沿着道路的 N 个邮箱的序列可以用一个长为 N 的由字母 A..Z 组成的字符串来表示。

某些邮箱可能会有相同的颜色。

约翰想要知道最小的 K 的值,使得他查看任意连续 K 个邮箱序列,他都可以唯一确定这一序列在道路上的位置。

例如,假设沿路的邮箱序列为 ABCDABC 。

约翰不能令 K=3,因为如果他看到了 ABC,则沿路有两个这一连续颜色序列可能所在的位置。

最小可行的 K 的值为 K=4,因为如果他查看任意连续 4 个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。

输入格式
输入的第一行包含 N,第二行包含一个由 N 个字符组成的字符串,每个字符均在 A..Z 之内。

输出格式
输出一行,包含一个整数,为可以解决农夫约翰的问题的最小 K 值。

数据范围
1≤N≤100
输入样例:
7
ABCDABC
输出样例:
4

代码与解析:

代码:
半暴力解法:
//最短不重复子序列,一定会有解,就是整个序列
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
const int N=110;
int main(){
    int n;
    string str;
    cin>>n>>str;
    for(int i=1;i<=n;i++){//枚举最小长度,如果满足条件即可输出
        unordered_set<string> st;
        bool flag=true;//每次枚举看是否能枚举完元素,能的话说明找到了目标长度i
        for(int j=0;j+i<=n;j++){
            string temp=str.substr(j,i);//从下标j开始,截取长度为i的字符串
            if(st.count(temp)){
                flag=false;
                break;
            }
            else
                st.insert(temp);
        }
        if(flag){
            cout<<i<<endl;
            break;
        }
    }
    return 0; 
}

二分优化解法:
#include<bits/stdc++.h>
using namespace std;
int n;
string str;
bool check(int mid)
{
    unordered_set<string> hash;
    for (int i=0; i+mid-1<str.size();i++){
        auto s=str.substr(i, mid);
        if(hash.count(s))return false;
        hash.insert(s);
    }
    return true;
}
int main()
{
    cin>>n;
    cin>>str;
    int l=1,r=n;
    while(l<r){
        int mid=l+r>>1;
        if (check(mid))r=mid;
        else l=mid+1;
    }
    cout<<r;
}

总结:

二分要注意的有两个:

1.边界情况的判定:做题只需要记住其中一种正确的方法就行,因为这些方法都是前辈们经过不断实践得出来的,绝对不会出现纰漏。我们学习知识绝大部分都是一个学习的过程,而非创新过程;

2.二分的二段性:如何去寻找二段性,应该是最难的一部分,也就是我们思考的过程;

刷题总结:

1.今天刷题发现做题时的思维不清晰,这是个很大的问题,虽然能做出来,但是效率低下,必须改进;

2.应该将重心放在对已学知识的巩固上,比如说将模板题多敲几遍,以及对代码的深入与拓展,多对自己问为什么;

3.以及及时回顾已复习知识点,避免遗忘,常看笔记。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

切勿踌躇不前

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

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

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

打赏作者

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

抵扣说明:

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

余额充值