蓝桥杯练习-3.9
视频学习
• 欧几里得算法
欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。
定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。
gcd(a,b) = gcd(b,a mod b)
要算 大数a 和 小数b 的最大公约数转化为求 小数b 和 余数a mod b的最大公约数。可以一直递归下去,直到不产生余数,即当b == 0时,a就是最大公约数。
int gcd(int a,int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
int gcd(int a,int b) {
return b ? gcd(b, a % b) : a;
}
代码练习
• H 等差数列(第十届蓝桥杯)
Description
数学老师给小明出了一道等差数列求和的题目。
但是粗心的小明忘记了一部分的数列,只记得其中N个整数。
现在给出这NN个整数,小明想知道包含这N个整数的最短的等差数列有几项?
Input
输入的第一行包含一个整数NN。
第二行包含NN个整数A1,A2,⋅⋅⋅,AN。(注意A_1∼A_NA1∼A**N并不一定是按等差数列中的顺序给出)
Output
输出一个整数表示答案。
Sample Input 1
5
2 6 4 10 20
Sample Output 1
10
Hint
【样例说明】
- 包含2、6、4、10、20的最短的等差数列是2、4、6、8、10、12、14、16、18、20。
【评测用例规模与约定】
- 对于所有评测用例,2≤N≤100000,0≤A_i≤{10}^92≤N≤100000,0≤Ai≤109。
思路
先给输入的整数排个序,因为要求构成最短的等差数列,所以实现排好的最大项和最小项一定是最终构成等差数列的最大项和最小项,因为如果在两边放,那么项数只可能更多。
已知项数 = (an-a1) / d + 1
所以公差d越大,项数越小
公差d一定可以整除数列中每一个数ai减第一个数a1,即:(ai-a1)%d = 0,则公差d最大为(ai-a1)的最大公因数
代码实现:
#include <iostream>
#include <algorithm>
using namespace std;
int a[100005];//用来存放输入整数
int b[100005];//原来存放ai - a1的差值
int Gcd(int a,int b)//求a和b最小公约数的函数
{
if(b == 0) return a;
return Gcd(b, a % b);
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a + 1, a + n + 1);
for (int i = 2; i <= n; i++)
{
b[i] =a[i] - a[1];
}
int d;//公差d
d = b[2];//实现规定公差d是b[i]数组中的第一个,分别后续比较
for (int i = 3; i <= n; i++)
{
d = Gcd(d, b[i]);
}
if(d)
cout << b[n]/d + 1 << endl;
else cout << n << endl;//公差为0的情况
return 0;
}
❕注意:我自己忽略的一点就是,本次开的a数组下标我是从1开始往后的,但是我的排序函数sort两个参数仍然是(a, a + n),所以导致我一直AC不了,实际上如果下标从1开始,也就是排1,到n的数的序,一定要记得,sort传的参数为(a + 1, a + n + 1),从0则是(a, a + n)。
• H 后缀表达式
【问题描述】
给定 N 个加号、M 个减号以及 N + M + 1 个整数 A 1 ,A 2 ,··· ,A N+M+1 ,小
明想知道在所有由这 N 个加号、M 个减号以及 N + M +1 个整数凑出的合法的
后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用1 2 3 + -,则 “2 3 + 1 -” 这个后缀表达式结果是 4,是最大的。
【输入格式】
第一行包含两个整数 N 和 M。
第二行包含 N + M + 1 个整数 A 1 ,A 2 ,··· ,A N+M+1 。
【输出格式】
输出一个整数,代表答案。
【样例输入】
1 1
1 2 3
【样例输出】
4
【评测用例规模与约定】
对于所有评测用例,0 ≤ N, M ≤ 100000,−10 9 ≤ A i ≤ 10 9 。
❕注意:后缀表达式中其实隐含了括号的存在,可以加任意的括号,
如果按照想法一,得到的答案为:2 + 3 - (-2) + (-1)
结果为6
但是还有有更优的方法:2 + 3 - ((-2) + (-1))
结果为8
思路
① 如果没有减号的话,全是加号,我们就把所有数加起来即可
② 如果有一个减号,那么我们可以转化为 …+…−(…+…+…)的形式,即分为两部分,中间一个减号,因此只要出现一个减号那么就可以视为出现一个或多个减号等同的效果。
③如果出现多个减号:也可以转化为…+…−(…+…−…)的形式,也就是说你希望它是减号时你可以把它放到括号外,你希望它是加号时,你可以把它放在括号里边,因为负负得正,因此,一个减号与多个减号可以视作一种情况。
总结一下就是:只要m > 0, 那么减号的数量实际上就是1 到n + m的任何一个数字。
所以我们有减号的时候,我们只需要找到最大值和最小值,因为无论如何,都会减号的数量至少为1,那么我们就减去那个最小值,加上最大值,剩下的数全部取绝对值加起来就行了,因为如果他们是负数,我们可以减去他。
代码实现
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
long long a[200005];//存入输入的所有整数
int main()
{
int n, m;//n代表有n个加号,m代表有m个减号
cin >> n >> m;
for (int i = 1; i <= n + m + 1; i++)
{
cin >> a[i];
}
long long sum;
if (m == 0)
{
sum = 0;
for (int i = 1; i <= n + m + 1; i++)
{
sum += a[i];
}
cout << sum << endl;
}
if (m > 0)
{
sum = 0;
sort(a + 1, a + n + m + 2);
for (int i = 2; i <= n + m; i++)
{
sum += abs(a[i]);
}
sum += a[n + m + 1];
sum -= a[1];
cout << sum << endl;
}
return 0;
}
❕注意:一定要注意数据的类型,选择int 还是long long,这直接影响到了答案!!!
• B 明码(第九届蓝桥杯)
Description
16点阵的字库把每个汉字看成是16x16个像素信息。并把这些信息记录在字节中。
一个字节可以存储8位信息,用32个字节就可以存一个汉字的字形了。
把每个字节转为2进制表示,1表示墨迹,0表示底色。每行2个字节,
一共16行,布局是:
第1字节,第2字节
第3字节,第4字节
…
第31字节, 第32字节
这道题目是给你一段多个汉字组成的信息,每个汉字用32个字节表示,这里给出了字节作为有符号整数的值。
题目的要求隐藏在这些信息中。你的任务是复原这些汉字的字形,从中看出题目的要求,并根据要求填写答案。
这段信息是(一共10个汉字):
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
16 64 16 64 34 68 127 126 66 -124 67 4 66 4 66 -124 126 100 66 36 66 4 66 4 66 4 126 4 66 40 0 16
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
0 -128 64 -128 48 -128 17 8 1 -4 2 8 8 80 16 64 32 64 -32 64 32 -96 32 -96 33 16 34 8 36 14 40 4
4 0 3 0 1 0 0 4 -1 -2 4 0 4 16 7 -8 4 16 4 16 4 16 8 16 8 16 16 16 32 -96 64 64
16 64 20 72 62 -4 73 32 5 16 1 0 63 -8 1 0 -1 -2 0 64 0 80 63 -8 8 64 4 64 1 64 0 -128
0 16 63 -8 1 0 1 0 1 0 1 4 -1 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 5 0 2 0
2 0 2 0 7 -16 8 32 24 64 37 -128 2 -128 12 -128 113 -4 2 8 12 16 18 32 33 -64 1 0 14 0 112 0
1 0 1 0 1 0 9 32 9 16 17 12 17 4 33 16 65 16 1 32 1 64 0 -128 1 0 2 0 12 0 112 0
0 0 0 0 7 -16 24 24 48 12 56 12 0 56 0 -32 0 -64 0 -128 0 0 0 0 1 -128 3 -64 1 -128 0 0
思路
“-1”转为二进制的过程,第一步:找原码“1”的8位二进制表示为00000001,第二步:找原码的反码为11111110,第三步:找补码(反码加一):11111111。完成。
代码实现
#include <iostream>
using namespace std;
int z[10];//用来存储每一个十进制数转化成二进制数的八位
void change(int x)
{
bool flag = false;
if (x > 0)//先判断进来的十进制数的正负,这关系到
{
flag = true;
}
else x = (-1) * x;
for (int j = 0; j < 8; j++)
{
z[j] = 0;//清空之前的数据
}
int num = 0;
do
{
z[num++] = x % 2;
x /= 2;
}while(x);//注意这里得到的数组按顺序排过去还并不是二进制的八位,而是颠倒的
if (flag == false)//如果是负数的话,那么你将它正数的原码变成补码,就是负数的原码
{
bool flag2 = false;
for (int i = 0; i < 7; i++)//找到第一个为1的数,因为是颠倒的,所以1的右边按位取反,1的左边不变
{
if(flag2 == true)
break;//找到了变完了,就退出循环
if (z[i] == 1)//找到了为1的
{
for (int j = i + 1; j < 7; j++)
{
flag2 = true;
if (z[j] == 1)
z[j] = 0;
else z[j] = 1;
}
}
else
{
continue;
}
}
z[7] = 1;//最后加上符号位
}
for (int i = 7; i >= 0; i--)
{
if (z[i] == 1)
{
cout << "*";
}
else cout << " ";
}
}
int main()
{
int a, b;
for (int i = 1; i <= 10; i++)
{
for (int j = 1; j <= 16; j++)
{
cin >> a >> b;
change(a);//每一个十进制数可以变为八位二进制数,一行需要16位,所以俩个就行
change(b);
cout << endl;
}
}
return 0;
}