第十五届蓝桥杯模拟赛第二期(C/C++组别)

题目解析为原创可能有误 仅供参考!!!

同时为了避免篇幅太长并没有给出太详细题解(大多数只是给出了模板)!!!

但是一些看不懂的算法我给出了名字(快速幂 算术基本定理... ...)可以自己查阅相应算法名称研究!!!

试题A

小蓝要在屏幕上放置一行文字,每个字的宽度相同。小蓝发现,如果每个字的宽为 36 像素,一行正好放下 30 个字,字符之间和前后都没有任何空隙。请问,如果每个字宽为 10 像素,字符之间不包含空隙,一行可以放下多少个字?

这道题目就很简单不做阐述!!!

#include<iostream>
using namespace std;
int main()
{
	cout << 30 * 36 / 10 << endl;
	return 0;
}
输出108

试题B

求 2*2023%1000,即 2的2023次方除以1000的余数。

这道题目可能很多同学直接pow(2,2023)%1000导致错误结果因为数据太大早就爆int了

我们在计算一堆数连乘的结果对一个数取余时需要每乘一次取余一次(不理解的同学百度搜索取模运算)

​
#include<iostream>
using namespace std;
int main()
{
	int res=2,p=1000;//res==result(结果的意思注意命名规范)
	for(int i=2;i<=2023;++i)
	{
		res=(res*2)%p;
	}
	cout<<res<<endl;
	return 0;
}
输出608
上面这种解法时间复杂度为线性复杂度O(N)
但是这道题目有一种通解也就是(快速幂)它可以将时间复杂度降为log(N)
(快速幂)是把指数用一个二进制数表示例如15变为1111这时只要操作4次就可以算出正确结果(因为只有四个位)而上面线性解法中则需要15次
下面给出快速幂模板
​
#include<iostream>
using namespace std;
typedef long long LL;
LL f(LL a, LL b, LL p)
{
    LL res = 1 % p;
    while(b)
    {
        if(b & 1) res = res * a % p;
        b = b >> 1;//等价b /= 2
        a = a * a % p;
    }
    return res;
}
int main()
{
    int n;
    cin >> n;
    while(n--)
    {
        LL a,b,p;
        cin >> a >> b >> p;
        cout << f(a, b, p) << endl;
    }
    return 0;
}

​
输出608

试题C

如果一个正整数转化成二进制与转换成八进制后所有数位的数字之和相等,则称为数位和相等的数。   

前几个数位和相等的正整数为 1, 8, 9, 64, ……   请问第 23 个数位和相等的正整数是多少?

这道题目只要能够正确统计八进制形式与二进制形式中的数字并且加和就可以了!!!
#include<iostream>
using namespace std;
int res;//定义在全局默认才为零哦! 
int main()
{
	for(int i = 1;i <= 1e9; ++ i)
	{
		int cnt1 = 0,cnt2 = 0;
		int t = i;
		while(t)
		{
			if(t%2) cnt1 += t%2;//如果取余不为0就加上 下面同理 
			t /= 2;
		}
		t = i;
		while(t)
		{
			if(t%8) cnt2 += t % 8;
			t /= 8;
		}
		if(cnt1 == cnt2) res ++;//统计 数位和相等的数的个数			 
		if(res == 23)//到第23位输出并且终止
		{
			cout << i << endl;
			break;
		}
	}
	return 0;
}
输出4169
自己尝试一下将代码输出改为以下内容

1 8 9 64 65 72 73 512 513 520 521 576 577 584 585 4096 4097 4104 4105 4160 4161 4168 4169

试题D

对于以下这些数(6行,每行6个,共36个),请问约数个数最多的是哪个?(如果有多个,请回答出现最早的那个)   

393353 901440 123481 850930 423154 240461
373746 232926 396677 486579 744860 468782
941389 777714 992588 343292 385198 876426
483857 241899 544851 647930 772403 109929
882745 372491 877710 340000 659788 658675
296521 491295 609764 718967 842000 670302

(读入时注意小心空格)

这道题目呢如果没有思路的话可以直接暴力算出每个数的约数因为只有36个数(记得带上纸笔哦)
下面为暴力代码
#include<iostream>
using namespace std;
int res;//还记得为什么要放在这里吗?
int main()
{
	int a;
	cin>>a;
	for(int i=1;i<=a;++i)
	{
		if(a%i==0) res++;
	}
	cout<<res<<endl;
	return 0;
}
正确答案为901440
当然咱们肯定不能什么题目都暴力(我们可是很温柔的)
下面给出通解
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
unordered_map<int,int> primes;
int res;//用来存储最终结果 
int a[6][6];//干脆就用这个二位数组来存吧  还记得为什么放在全局吗? 
/*
393353 901440 123481 850930 423154 240461
373746 232926 396677 486579 744860 468782
941389 777714 992588 343292 385198 876426
483857 241899 544851 647930 772403 109929
882745 372491 877710 340000 659788 658675
296521 491295 609764 718967 842000 670302
*/
//直接复制这里的就好没有多余空格了 
int cal(int x)
{
    for(int i=2;i<=x/i;++i)
    {
        while(x%i==0)
        {
            primes[i]++;
            x/=i;
        }
    }
    if(x>1) primes[x]++;
    int res=1;//局部和全局并不冲突哦 
    for(auto t:primes)//注意你的编译器可能不支持c++11写法 
    {//devc 为例  工具---编译选项---编译时加如以下命令--输入  -std=c++11  确定--这样就不会报错了 
        res=res*(t.second+1);
    }
    primes.clear();
    return res;
} 
int main()
{
    for(int i=0;i<6;++i)
	{
		for(int j=0;j<6;++j)
		{
			cin>>a[i][j];
			res=max(res,cal(a[i][j]));//找到约数最大的那个数的约数是多少 
		}
	}
	for(int i=0;i<6;++i)
	{
		for(int j=0;j<6;++j)
		{
			if(cal(a[i][j])==res)//再找一遍 输出他 
			{
				cout<<a[i][j];
                break;//防止有重复的
			}
		}
	} 
    return 0;
}
输出901440
自己尝试一下能不能对代码稍加改动输出以下内容呢?

393353 901440 123481 850930 423154 240461
6 84 8 8 8 8
373746 232926 396677 486579 744860 468782
16 8 8 8 12 8
941389 777714 992588 343292 385198 876426
4 16 12 12 8 24
483857 241899 544851 647930 772403 109929
4 8 6 8 2 4
882745 372491 877710 340000 659788 658675
4 8 32 60 12 6
296521 491295 609764 718967 842000 670302
4 16 6 4 40 16

下面稍稍给出一点证明
首先根据算术基本定理(又名唯一分解定理)我们可以知道任何一个大于1的自然数都可以被一堆素数的连乘表示出来(并且将这些素数按顺序排列方式唯一(所以又叫唯一分解定理))例如:100=2*2+5*2-----排列后2 2 5 5

也就是说答案answer为分解后的每个素数的指数加1再连乘比如上面的100则为(2+1)*(2+1)

试题E

小蓝有一个01矩阵。他打算将第一行第一列的 0 变为 2 。变化过程有传染性,每次 2 的上下左右四个相邻的位置中的 0 都会变成 2 。直到最后每个 2 的周围都是 1 或 2 结束。   请问,最终矩阵中有多少个 2 ?   以下是小蓝的矩阵,共 30 行 40 列。   

       0000100010000001101010101001001100000011   0101111001111101110111100000101010011111   1000010000011101010110000000001011010100   0110101010110000000101100100000101001001   0000011010100000111111001101100010101001   0110000110000000110100000000010010100011   0100110010000110000000100010000101110000   0010011010100110001111001101100110100010   1111000111101000001110010001001011101101   0011110100011000000001101001101110100001   0000000101011000010011111001010011011100   0000100000011001000100101000111011101100   0010110000001000001010100011000010100011   0110110000100011011010011010001101011011   0000100100000001010000101100000000000010   0011001000001000000010011001100101000110   1110101000011000000100011001001100111010   0000100100111000001101001000001010010001   0100010010000110100001100000110111110101   1000001001100010011001111101011001110001   0000000010100101000000111100110010101101   0010110101001100000100000010000010110011   0000011101001001000111011000100111010100   0010001100100000011000101011000000010101   1001111010010110011010101110000000101110   0110011101000010100001000101001001100010   1101000000010010011001000100110010000101   1001100010100010000100000101111111111100   1001011010101100001000000011000110110000   0011000100011000010111101000101110110001

这道题目呢典型的flood fill 算法(其实也就是个bfs(宽度优先搜索也叫广度优先搜索简称宽搜))
下面给出详细ac代码(注释里的01可以直接拷贝没有多余空格哦)
/*
0000100010000001101010101001001100000011
0101111001111101110111100000101010011111
1000010000011101010110000000001011010100
0110101010110000000101100100000101001001
0000011010100000111111001101100010101001
0110000110000000110100000000010010100011
0100110010000110000000100010000101110000
0010011010100110001111001101100110100010
1111000111101000001110010001001011101101
0011110100011000000001101001101110100001
0000000101011000010011111001010011011100
0000100000011001000100101000111011101100
0010110000001000001010100011000010100011
0110110000100011011010011010001101011011
0000100100000001010000101100000000000010
0011001000001000000010011001100101000110
1110101000011000000100011001001100111010
0000100100111000001101001000001010010001
0100010010000110100001100000110111110101
1000001001100010011001111101011001110001
0000000010100101000000111100110010101101
0010110101001100000100000010000010110011
0000011101001001000111011000100111010100
0010001100100000011000101011000000010101
1001111010010110011010101110000000101110
0110011101000010100001000101001001100010
1101000000010010011001000100110010000101
1001100010100010000100000101111111111100
1001011010101100001000000011000110110000
0011000100011000010111101000101110110001
*/
#include<iostream>
#include<string>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int ,int>PII;//我比较习惯用 pair 来存一对二元组 
string s[30];//用来存地图01 
int res;//还记得为什么变量放在全局吗? 
int bfs(int a,int b)//典型bfs模板 大家可以记忆一下多练几遍 
{
    queue<PII> q;//stl自带队列 
    q.push({a,b});
    s[a][b]='2';
    res++;//起点变为2 
    while(q.size())
    {
        PII t=q.front();
        q.pop();
        int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};//上下左右四个偏移量 
        for(int i=0;i<4;++i)
        {
            int x=t.first+dx[i],y=t.second+dy[i];//偏移后的新坐标 
            if(x>=0&&x<30&&y>=0&&y<40&&s[x][y]=='0')//符合条件的话入队 
            {
            	s[x][y]='2'; 
            	q.push({x,y});
            	++res;
			}
        }
    }
    return res; 
}
int main()
{
	for(int i=0;i<30;++i) cin>>s[i];
    cout<<bfs(0,0)<<endl;//从起点开始宽搜  并且返回答案 
    
    return 0;
}
输出541

试题F

问题描述   

        给定一个正好六位的正整数 x,请将 x 循环左移一位后输出。   

        所谓循环左移一位,是指将原来的十万位变为个位,原来的万位到个位向左移动依次变为十万 位到十位。   

        例如:194910 左移一位变为 949101 。  

        又如:987123 左移一位变为 871239 。

输入格式

        输入一行包含一个整数 x 。保证输入的 x 正好包含 6 个十进制数位,而且十万位和万位上的数 字均不为 0 。

输出格式   

        输出一行包含一个整数,表示答案。 样例输入 194910 样例输出 949101

这道题目过于简单直接给答案
#include<iostream>
using namespace std;
int main()
{
	int a;
	cin>>a;
	cout<<a%100000<<a/100000<<endl;
    return 0;
}

试题G

问题描述   

        输入一个仅包含小写英文字母的字符串,请问这个字符串中的最后一元音是什么。

        在英文中,a, e, i, o, u 共 5 个字母是元音字母,其它字母不是元音字母。

输入格式   

        输入一行包含一个字符串,仅由小写英文字符组成,字符串中至少包含一个元音字母

输出格式

        输出一行包含一个字符,表示答案。

样例输入

        lanqiao

样例输出

        o

样例输入

        cup

样例输出

        u

没想到吧这题倒着写更快呦(过于煎蛋直接给答案)
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s;
	cin>>s;
	for(int i=s.size()-1;i;--i)
	{
		if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u')
		{
			cout<<s[i]<<endl;
			break;
		}
	}
    return 0;
}

试题H

问题描述   

        给定一个整数,对这个整数的一次转换是指将这个整数变为这个整数的所有数位上的非零数字的乘积。   

        例如,对 123456789 进行一次转换变为 123456789=362880,再进行一次转换变为 36288=2304,再进行一次转换变为 234=24,再进行一次转换变为 8。   

        给定一个整数,请依次将转换过程中经历的每个整数输出,直到小于 10 。

输入格式   

        输入一行包含一个整数 n 。

输出格式   

        输出多行,每行包含一个整数。

样例输入

        123456789

样例输出

        362880

        2304

        24

        8

评测用例规模与约定   

        对于 50% 的评测用例,1 <= n <= 10^9 (10的9次方)。   

        对于所有评测用例,1 <= n <= 10^18 (10的18次方)。

相信大家自己就可以写出来这题但是注意有坑
数据范围要开long long哦!!!
#include<iostream>
using namespace std;
typedef long long LL;
int main()
{
	LL a;
	cin>>a;
	while(a)
	{
		LL t=a;
		LL res=1;
		while(t)
		{
			if(t%10) res=res*(t%10);
			t/=10;
		}
		cout<<res<<endl;
		a=res;
		if(res<10) break;
	}
    return 0;
}

试题I

问题描述   

        小蓝站在一个 n 行 m 列的方格图中间,方格图的每一个方格上都标有一个正整数。      如果两个相邻方格(上下左右四个方向相邻)内的数的最大公约数大于 1 ,则可以从其中一个 方格移动到另一个方格,当然也可以从另一个方格移回第一个方格。   

        假设小蓝开始时站在第 r 行第 c 列,请问小蓝可以移动到方格图内的多少个方格?

输入格式   

        输入的第一行包含两个整数 n, m ,用一个空格分隔,表示方格图的行数和列数。   接下来 n 行,每行包含 m 个正整数,相邻整数间用一个空格分隔,依次表示方格图中从第 1 行到第 n 行,每行从第 1 列到第 m 列中的数。   

        接下来一行包含两个整数 r, c,用一个空格分隔,表示小蓝所在的行号和列号。

输出格式   

        输出一行包含一个整数,表示答案。

样例输入 

3 4

3 6 5 5

2 4 3 5

7 8 3 8

3 2

样例输出 5

评测用例规模与约定   

        对于50%的评测用例,1 <= n, m <= 100,方格图中的每个数不超过 10^5 (10的5次方)。   

        对于所有评测用例,1 <= n, m <= 1000,方格图中的每个数不超过 10^9 (10的9次方)。

这道题目可能是众多简单题目里面稍难的一道题目了
大家注意试题E!!!!!!!!!!!!!!!!!!(只是多加了一个条件罢了就是gcd函数)
一模一样的bfs算法()
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int ,int>PII;
const int N=1010;//注意数据范围最大1000*1000我们这里稍开大一些 
//可以避免很多的边界问题 
int g[N][N];//用来存图 graph
int f[N][N];//判重数组 
int n,m;//行 列  还记得为什么变量放在这里吗?
int res;//统计答案数目 
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
void bfs(int a,int b)
{
    queue<PII> q;
    q.push({a,b});
    f[a][b]=1;//打个标记这个点以后不能再走了
			//防止回到走过的点从而死循环 
	res++;//脚下也是一个点 
    while(q.size())
    {
        PII t=q.front();
        q.pop();
        int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};//熟悉的偏移数组 
        for(int i=0;i<4;++i)
        {
            int x=t.first+dx[i],y=t.second+dy[i];//上下左右枚举四个方向 
            if(x>=0&&x<n&&y>=0&&y<m&&!f[x][y]&&gcd(g[t.first][t.second],g[x][y])>1)
            {
            	//就是多了个gcd>1的条件
				//!f[x][y]是 f[x][y]==0还没走过
				f[x][y]=1;//这时就走过了 
				q.push({x,y});//入队新坐标继续搜
				res++; 
			}
        }
    }
}
int main()
{
    cin>>n>>m;
	for(int i=0;i<n;++i)
	{
		for(int j=0;j<m;++j)
		scanf("%d",&g[i][j]);//为什么突然用scanf了呢?
		//因为数据最多1000*1000达到百万级别了
		//要注意一般超过十万的输入输出我们就不再用
		//cout cin 而是用printf scanf因为格式化输入输出更快 
	}
	int a,b;//所在的起点不是E题是在固定的1行1列了 
	cin>>a>>b;
	bfs(a-1,b-1);//因为下标我们从零开始的所以减1
	
    cout<<res<<endl; 
    return 0;
}
输出5
gcd---great common divisor 最大公约数
上面用到的方法(名字为 “辗转相除法”又名“欧几里得算法”)
给出名字自行百度查找证明哦

试题J

问题描述   

        给定一个序列 a[1], a[2], …, a[n] 和一个整数 k,请找出一个长度正好为 k 的区间,使得区间中 所有数的和最大。   

        即要找到一个整数 p ,使得 1 <= p 且 p+k-1 <= n ,使得 a[p]+a[p+1]+...+a[p+k-1] 最大。

输入格式   

        输入的第一行包含两个整数 n , k。   

        第二行包含 n 个整数,相邻的整数之间使用一个空格分隔,表示给定的序列。

输出格式   

        输出一行包含一个整数,表示最大的区间和,你只需要输出和就行,不需要输出方案。 样例输入

         6 3 2 3 9 1 9 5

样例输出

        19

评测用例规模与约定   

        对于 30% 的评测用例,1 <= k <= n <= 30,1 <= a[i] <= 100。   

        对于 60% 的评测用例,1 <= k <= n <= 1000,1 <= a[i] <= 10000。   

        对于所有评测用例,1 <= k <= n <= 100000,1 <= a[i] <= 1000000。

典型的前缀和问题
什么是前缀和
原数组: a[1], a[2], a[3], a[4], a[5], …, a[n]
前缀和 Si为数组的前 i项和
前缀和: S[i] = a[1] + a[2] + a[3] + … + a[i]
注意: 前缀和的下标一定要从 1开始, 避免进行下标的转换
s[0] = 0
s[1] = a[1]
s[2] = a[1] + a[2]
前缀和的作用
快速求出元素组中某段区间的和
一维数组求解前缀和(Si)
for循环求出 每个S[i] (将 S[0] 定义为 0, 避免下标的转换)
求 [l, r]中的和, 即为 S[r] - S[l-1]
但是本题有坑注意数据范围最坏情况下每个数都是1000000一共100000个数和为10*11爆int喽
要开long long 呦
下面给出完整代码
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1000010;
int a[N];
int s[N];//方便大家理解开两个数组 
int res;//答案 
int main()
{
    int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);//原数组注意用的scanf哦?
	//为什么不用cin呢? 
	for(int i=1;i<=n;++i) s[i]+=s[i-1]+a[i];//构建前缀和 
	
	for(int i=1;i<=n-k+1;++i)//线性扫描一边找区间最大值 
		res=max(res,s[i+k-1]-s[i-1]);
	
	cout<<res<<endl;
    return 0;
}
看到这里一定很不容易吧(我也是弄了三个小时累死了)
祝屏幕前的你明年取得好成绩哦
  • 60
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值