蓝桥杯学习笔记(一)

零碎知识点:

输入输出的数据 <105 使用 cin cout

输入输出的数据 >=105 使用 scanf printf

220 ≈ 106 216 = 65536 215 = 32268 263 ≈ 1018

头文件大礼包

#  include <cstdio>
#  include <cstring>
#  include <iostream>
#  include <algorithm>

memcpy(str1,str2,sizeof str2); 将数组str2的值全部赋值给str1

memset(str1,0,sizeof str1); 将数组str1每个值都设为0

求出现次数技巧:

输入一串字母,求这串字母中出现次数最多的字母

一般情况下我们会用最简单的暴力解法直接用多次循环统计每个字母的次数然后比较

统计字母次数我们有一个更简单的方法:

for(int i = 0; i < n; i++)
{
	scanf("%c", &str1[i]);
    str2[str1[i]]++;
}

用这个方法直接找出str2中最大的一个数字的下标(下标所对应的数字即为这个数字的ascall码所对应的字母)

符号 描述 运算规则
& 两个位都为1时,结果才为1
| 两个位都为0时,结果才为0
^ 异或 两个位相同为0,相异为1
~ 取反 0变1,1变0
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0
>> 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

快速取出一个多位整数各个数位上的方法

不断用10对这个整数取余和除法运算

int b = 12345;
int str[5];

int main()
{
    int i = 0;
    while(b)
    {
        str[i] = b%10;
        b /= 10;
        i++;
    }
}

题目:

带分数——递归

(第四届蓝桥杯省赛C++B/C组,第四届蓝桥杯省赛JAVAA/B组)

100100 可以表示为带分数的形式:100=3+69258/714

还可以表示为:100=82+3546/197

注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含0)。

类似这样的带分数,100 有 11 种表示法。

输入格式

一个正整数。

输出格式

输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1≤N<106

输入样例1:

100

输出样例1:

11

解题思路:

题目形式为 n = a + b/c ,a,b,c,n 都是整数并且数字 1∼9 分别出现且只出现一次

可以直接枚举abc所有可能然后进行判断,但计算量过大

可以转化题目形式 n = a + b/c => b = nc - ac ,只需枚举ac的值算出b的值

根据b的结果判断是否满足题目要求

飞行员兄弟——递推

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 16 个把手的冰箱。

已知每个把手可以处于以下两种状态之一:打开或关闭。

只有当所有把手都打开时,冰箱才会打开。

把手可以表示为一个 4×4 的矩阵,您可以改变任何一个位置 [i,j] 上把手的状态。

但是,这也会使得第 i 行和第 j 列上的所有把手的状态也随着改变。

请你求出打开冰箱所需的切换把手的次数最小值是多少。

输入格式

输入一共包含四行,每行包含四个把手的初始状态。

符号 + 表示把手处于闭合状态,而符号 - 表示把手处于打开状态。

至少一个手柄的初始状态是关闭的。

输出格式

第一行输出一个整数 N,表示所需的最小切换把手次数。

接下来 N 行描述切换顺序,每行输出两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。

数据范围

1≤i,j≤4

输入样例:

-+--
----
----
-+--

输出样例:

6
1 1
1 3
1 4
4 1
4 3
4 4

解题思路:

首先需要知道每一个门把手只需要操作一次,操作两次则会还原到原本的状态

对于16个把手,每个把手都有开和关两个状态,可以用十六位的二进制来表示所有把手我们需要操作的方案

用1表示需要切换,0表示不变,例如0000000000001111表示第一排的把手需要切换,其余全不变(从右往左)

枚举二进制中能够出现的所有操作,对每种操作进行判断是否能够还原输入样例中的数据

关于如何检测到二进制中所有的1,,可以用位运算 >> ,让二进制数右移一位然后和1求 &

如果是1则切换此位置中行和列所有的把手

当某一种方案可以使把手全部打开,则记录此数据的步数,找出最小的那个步数输出

数的范围——二分

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

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

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

输入格式

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

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

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

输出格式

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

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

数据范围

1≤n≤100000
1≤q≤10000
1≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1

解题思路:

二分法有三个关键指针,指向区间最左边的 l ,指向区间最右边的 r ,指向区间中间的 mid

将所要查找的数值与指针 mid 对应的数值比大小,通过移动 l 或者 r 缩小区间范围

直到最后 l == r (找到目标)或者 l < r (不存在目标)停止

而这个题目需要找到的是一个数的范围,即起始位置和结束位置

需要确定 l 或 r 是移动到 mid + 1 还是 mid - 1 或者是 mid 的位置

用两次二分分别求起始位置和结束位置即可

K倍区间——前缀

第八届蓝桥杯省赛C++B组,第八届蓝桥杯省赛JAVAB组

给定一个长度为 N 的数列,A1,A2,…AN 如果其中一段连续的子序列 Ai,Ai+1,…Aj之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。

你能求出数列中总共有多少个 K 倍区间吗?

输入格式

第一行包含两个整数 N 和 K。

以下 N 行每行包含一个整数 Ai。

输出格式

输出一个整数,代表 K 倍区间的数目。

数据范围

1≤N,K≤1000001
1≤Ai≤100000

输入样例

5 2
1
2
3
4
5

输出样例

6

解题思路:

前缀和指的是一个区间里面所有数的和

如果 每次计算都要求一次对应区间的和会增加耗费时间

所以 我们可以用空间换时间

例如:

我们用循环输入了一个数组,然后可以创建一个数组在每次输入数到数组的同时计算前缀和到对应下标的新数组中

for(int i = 1; i <=5; i++)

{

​ a[i] = i;

​ sum[i] = s[i-1] + a[i];

}

最终的结果为a[5] = {0,1,2,3,4,5} s1[5] = {0,1,3,6,10,15}

如果要计算对应区间中的数字,我们可以用减法直接计算

例如:

求[2,5]区间中的和 可以通过 s1[5] - s1[1] 来得到

对于这个题目找 K 倍区间,直接用暴力解决会消耗非常大的时间,这里有一个巧妙的解法

例如我们需要求 2 倍区间,我们将 s1[] 中的每个数字对2进行取余,得到的结果为

s2[5] = {0,1,1,0,0,1}

当有重复的数字出现时,这两个数字对应的区间即为 2 倍区间

即 s2[5] = s2[1] = 1 -> s1[5] - s1[1] = 15 - 1= 14

统计所有 2 倍区间的个数即为此题的解

posted @ 2023-02-22 19:29  Shadow-Fy  阅读(40)  评论(0编辑  收藏  举报