蓝桥杯-二分专题(包含历年蓝桥杯真题和详细注释)

文章提供了几道编程竞赛题目,包括通过邮箱颜色序列确定位置的最小连续子串长度,数组中元素查询的二分查找方法,以及如何公平地将长方形巧克力分割成正方形给小朋友。这些问题涉及到字符串处理、排序和搜索算法的应用。
摘要由CSDN通过智能技术生成

目录

我在哪?

数的范围

 四平方和(第七届蓝桥杯省赛C++A/B组,第七届蓝桥杯省赛JAVAB/C组) 

分巧克力(第八届蓝桥杯省赛C++A/B组,第八届蓝桥杯省赛JAVAA/B组)


我在哪?

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

沿路有一排共 NN 个农场。

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

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

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

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

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

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

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

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

输入格式

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

输出格式

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

数据范围

1≤N≤1001≤N≤100

输入样例:

7
ABCDABC

输出样例:

4
#include <iostream>
#include <cstring>
#include <unordered_set>
#include <algorithm>
using namespace std;
int n;
string s;
bool check(int mid) {
    unordered_set<string>hash;//用来存储字符串是否出现过
    for (int i = 0; i + mid - 1 < n; i++)//从i=0开始枚举 长度为mid的子串
    {
        auto str = s.substr(i, mid);//截取字符串
        if (hash.count(str))return false;//已经出现过 不符合条件
        hash.insert(str);// 未出现过 加入字符串
    }
    return true;
}
int main()
{

    cin >> n >> s;
    int l = 1, r = n;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid))r = mid;//当前符合条件 就可以继续往左区间查找更小的答案
        else l = mid + 1;//不符合条件 往右区间寻找答案
    }
    cout << l << endl;
    return 0;

}

数的范围

给定一个按照升序排列的长度为 nn 的整数数组,以及 qq 个查询。

对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 00 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

输入格式

第一行包含整数 nn 和 qq,表示数组长度和询问个数。

第二行包含 nn 个整数(均在 1∼100001∼10000 范围内),表示完整数组。

接下来 qq 行,每行包含一个整数 kk,表示一个询问元素。

输出格式

共 qq 行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回 -1 -1

数据范围

1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<m;i++)
    {
        int target;
        cin>>target;
        int l=0,r=n-1;
        //二分寻找左端点
        while(l<r)
        {
            int mid=l+r>>1;
            if(a[mid]>=target)r=mid;
            else l=mid+1;
        }
        //没搜索到等于target的左端点代表没有等于target的数字  因此直接输出-1 -1
        if(a[l]!=target)cout<<"-1 -1"<<endl;
        else {
            cout<<l<<' ';
            int l=0,r=n-1;
            //寻找右端点
            while(l<r){
                int mid=l+r+1>>1;
                if(a[mid]<=target)l=mid;
                else r=mid-1;
            }
            cout<<l<<endl;
        }
    }
    return 0;
}

 四平方和(第七届蓝桥杯省赛C++A/B组,第七届蓝桥杯省赛JAVAB/C组) 

四平方和定理,又称为拉格朗日定理:

每个正整数都可以表示为至多 44 个正整数的平方和。

如果把 00 包括进去,就正好可以表示为 44 个数的平方和。

比如:

5=02+02+12+225=02+02+12+22
7=12+12+12+227=12+12+12+22

对于一个给定的正整数,可能存在多种平方和的表示法。

要求你对 44 个数排序:

0≤a≤b≤c≤d0≤a≤b≤c≤d

并对所有的可能表示法按 a,b,c,da,b,c,d 为联合主键升序排列,最后输出第一个表示法。

输入格式

输入一个正整数 NN。

输出格式

输出4个非负整数,按从小到大排序,中间用空格分开。

数据范围

0<N<5∗1060<N<5∗106

输入样例:

5

输出样例:

0 0 1 2
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 5000010;
int C[N],D[N];//用C[s]D[s] 来存储 c*c+d*d可以等于s的c和d
int n;
int main()
{
    cin>>n;
    memset(C, -1, sizeof C);//判断是否是最小字典序的解 也就是 是否是第一个解
    for(int c=0;c*c<=n;c++)
    {
        for(int d=c;c*c+d*d<=n;d++)
        {
            int s=c*c+d*d;
            if(C[s]==-1){
                C[s]=c;D[s]=d;
            }
        }
    }
    //因为ab从小到大枚举 判断n-(c的平方和+b平方和) ==(c的平方和+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;//求出c和d平方和应该等于得数
            if(C[s]!=-1)//判断是否存在
            {
                cout<<a<<' '<<b<<' '<<C[s]<<' '<<D[s]<<endl;
                return 0;
            }
        }
    }
    return 0;
}

分巧克力(第八届蓝桥杯省赛C++A/B组,第八届蓝桥杯省赛JAVAA/B组)

儿童节那天有 KK 位小朋友到小明家做客。

小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 NN 块巧克力,其中第 ii 块是 Hi×WiHi×Wi 的方格组成的长方形。

为了公平起见,小明需要从这 NN 块巧克力中切出 KK 块巧克力分给小朋友们。

切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同

例如一块 6×56×5 的巧克力可以切出 66 块 2×22×2 的巧克力或者 22 块 3×33×3 的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入格式

第一行包含两个整数 NN 和 KK。

以下 NN 行每行包含两个整数 HiHi 和 WiWi。

输入保证每位小朋友至少能获得一块 1×11×1 的巧克力。

输出格式

输出切出的正方形巧克力最大可能的边长。

数据范围

1≤N,K≤1051≤N,K≤105,
1≤Hi,Wi≤1051≤Hi,Wi≤105

输入样例:

2 10
6 5
5 6

输出样例:

2
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10;
typedef long long LL;
int h[N],w[N];
int n,k;
bool check(int mid)
{
    int ans=0;//可以分给几个小朋友
    for(int i=0;i<n;i++)//枚举每一个巧克力
    {
        ans+=(LL)(h[i]/mid)*(w[i]/mid);
        if(ans>=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;//最小值是1 因为确保每个人最少可以1*1 最大值1e5 因为h和w最大是1e5
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(check(mid))l=mid;//表示当前mid 方案可行  所以答案最小为mid  可以继续尝试是否还能分的更大
        else r=mid-1;//不满足则取左区间
    }
    cout<<l<<endl;
    return 0;
} 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值