第二周周测题解

前言

这套题中涉及到的知识点有 模拟、前缀和、贪心、快速排序、动态规划、结构体、栈、队列、dfs、bfs等。题目偏简单。
在写代码时,可以用函数来实现单独的功能,比如第二题实现一个判断素数的函数,第三题实现一个将数字分解相加并乘以3加1的函数等。能使代码变得简洁明了, 方便后面的调试和修改。

7-0 当日天数

昨天热身赛的最后一题,但很多人没写出来,计算日期的这类问题,蓝桥杯基本每年都会考,所以就写一下思路和题解供参考。

编程计算某年某月某日当天,是当年的第几天。 说明:1)当年1月1日是第1天;2)每月按月大,月小,闰年等情况,各月天数有不同(31,30,28,29);3)闰年按4年一闰,100年不闰,400年再闰计算。

输入格式:
用空格分隔的三个正整数(表示年,月,日的整数),均是合法的数据。

输出格式:
直接输出年-月-日:整数。表示当日是第几天。年号占4位,不足4位时补前导零,月和日各占2位,不足2位也补前导零。

输入样例:

2020 3 3

输出样例:

2020-03-03:63

首先是格式,使用printf(%02d, month); d前面加一个02的意思是 如果数字不够2位数的话就在前面补0,scanf 与printf 还有许多用法,可以在网上或者自己试验,多了解一下。
题目大致思路是:模拟每一天的增加,到达月底最后一天,月份加一,月份到达年底最后一个月,年份加一,注意每过一年还需要判断一下是不是闰年。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//用一个数组存每个月有多少天
int arr[13] = {
    
    0,31,28,31,30,31,30,31,31,30,31,30,31};

int main(){
    
    
	int y, m, d, ans = 1;
	cin >> y >> m >> d;
	//闰年的话2月就是29天,注意如果题目是遍历好多年的,每过一年就把arr[2]变为28,防止出错。
	if(y%4==0&&y%100!=0||y%400==0) arr[2] = 29;
	int i = 1, j = 1; // 记录月和天
	while(1){
    
    
		if(i == m && j==d) break;
		j++; ans++;
		// 如果天数大于当月的天数, 月数加一,天数变为0。
		// 如果月数大于12,年数加一,月数变为0,本题不需要。
		if(j > arr[i]){
    
    
			i++, j = 1;
		}
	}
	printf("%04d-%02d-%02d:%d",y,m,d,ans);
}

7-1 西安距离

小明来到了古都西安,想去参观大唐西市!
西安的道路可以看做是与x轴或y轴垂直的直线,小明位于(a,b),而目的地位于(c,d),问最少几步可以到达。

输入格式:
一行中四个整数,a,b,c,d,表示坐标为(a,b)与(c,d),这里0<=a,b,c,d<=1000

输出格式:
输出这两个点的西安距离。

输入样例:

0 0 3 4

输出样例:

7

题目中要求只能走与x轴或y轴水平的直线,因此不是求两点之间的直线距离,而求|(x2-x1)|+|(y2-y1)|的值。注意求绝对值。代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    
    
	int a, b, c, d;
	cin >> a >> b >> c >> d;
	cout << abs(c-a)+abs(d-b);
}

7-2 判断素数

本题的目标很简单,就是判断一个给定的正整数是否素数。

输入格式:
输入在第一行给出一个正整数N(≤ 10),随后N行,每行给出一个小于2^31​​的需要判断的正整数。

输出格式:
对每个需要判断的正整数,如果它是素数,则在一行中输出Yes,否则输出No。

输入样例:

2
11
111

输出样例:

Yes
No

判断一个数是否为素数只需遍历到 n \sqrt{n} n 即可,注意1不是素数。代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int jud(int n){
    
    
	if(n == 1) return 0;
	for(int i = 2; i <= sqrt(n); i++){
    
    
		if(n%i == 0) return 0;
	}
	return 1;
}

int main(){
    
    
	int n, a;
	cin >> n;
	while(n--){
    
    
		cin >> a;
		if(jud(a)) cout << "Yes" << endl;
		else cout << "No" << endl; 
	}
}

7-3 掉入陷阱的数字

对任意一个自然数N0,先将其各位数字相加求和,再将其和乘以3后加上1,变成一个新自然数N1​ ;然后对N1重复这种操作,可以产生新自然数N​2 ;……多次重复这种操作,运算结果最终会得到一个固定不变的数N​k,就像掉入一个数字“陷阱”。
本题要求对输入的自然数,给出其掉入“陷阱”的过程。

输入格式:
在一行内给出一个自然数N​0(N​0 < 30000)。

输出格式:
对于输入的N​0,逐行输出其掉入陷阱的步骤。第i行描述N掉入陷阱的第i步,格式为: i:N​i(i≥1)。当某一步得到的自然数结果N​k(k≥1)与上一步N
​k−1​​ 相同时,停止输出。

输入样例:

5

输出样例:

1:16
2:22
3:13
4:13

用一个变量记录上一次的数字是多少,如果这次跟上次相同就结束。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

//将n各位数字相加求和.再将其和乘以3后加上1
int resolve(int n){
    
    
	int ans = 0;
	while(n){
    
    
		ans += n%10;
		n /= 10;
	}
	return 3*ans+1;
}

int main(){
    
    
	int n, f;  // f用来记录上一次的数字。
	cin >> n;
	for(int i = 1; ; i++){
    
    
		f = n;
		n = resolve(n);
		cout << i << ":" << n << endl;
		if(f == n) break;
	}
	return 0;
}

7-4 跳一跳

微信小程序中的跳一跳相信大家都玩过。emmm???只学习不玩游戏?那就吃亏了…好好读题理解吧。   
简化后的跳一跳规则如下:玩家每次从当前方块跳到下一个方块,如果没有跳到下一个方块上则游戏结束。   
如果跳到了方块上,但没有跳到方块的中心则获得1分;
跳到方块中心时,若上一次的得分为1分或这是本局游戏的第一次跳跃则此次得分为2分,否则此次得分比上一次得分多两分(即连续跳到方块中心时,总得分将+2,+4,+6,+8…)。   
现在给出一个人跳一跳的全过程,请你求出他本局游戏的得分(按照题目描述的规则)。

输入格式:
输入包含多个数字,用空格分隔,每个数字都是1,2,0之一,
1表示此次跳跃跳到了方块上但是没有跳到中心,
2表示此次跳跃跳到了方块上并且跳到了方块中心,
0表示此次跳跃没有跳到方块上(此时游戏结束)。
对于所有评测用例,输入的数字不超过30个

输出格式:
 输出一个整数,为本局游戏的得分(在本题的规则下)。

输入样例:

1 1 2 2 2 1 1 2 2 0

输出样例:

22

模拟即可。用一个变量标记连续几次跳到中心,没跳到中心就清0。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    
    
	int n, ans = 0, f = 1;
	while(cin >> n, n!=0){
    
    
		if(n == 2) {
    
    
			ans += f*2; f++;
		}else{
    
    
			ans++; f = 1;
		}
	}
	cout << ans; 
}

7-5 看电影

终于到周末了,明明是特别喜欢看电影。他想在一天内尽量多的看到完整的多部电影。 现在他把他喜欢的电影的播放时间表给你,希望你能帮他合理安排。

输入格式:
输入包含多组测试数据。每组输入的第一行是一个整数n(n<=100),表示明明喜欢的电影的总数。 接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个电影的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。 当n=0时,输入结束。

输出格式:
对于每组输入,输出能完整看到的电影的个数。

输入样例:
在这里给出一组输入。例如:

12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0

输出样例:

5

贪心算法,根据电影的结束时间从小到大排序,用一个变量记录当前时间,遍历寻找满足 电影开始时间>=当前时间的电影,寻找的第一个符合条件的电影就是最优的选择,再将记录时间的变量改为此电影的结束时间。直至遍历结束。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105;

struct node{
    
    
	int begin, end;
}arr[N];

bool cmp(node a, node b){
    
    
	if(a.end == b.end){
    
    
		return a.begin < b.begin;
	}
	return a.end < b.end;
}

int main(){
    
    
	int n;
	while(cin >> n, n!=0){
    
    
		int ans = 0, f = 0; // f用来记录当前的时间
		for(int i = 0; i<n; i++){
    
    
			cin >> arr[i].begin >> arr[i].end;
		}
		sort(arr,arr+n,cmp);
		//遍历寻找
		for(int i = 0; i < n; i++){
    
    
			if(arr[i].begin >= f){
    
    
				ans++;
				f = arr[i].end;
			}
		}
		cout << ans << endl;
	}

}

7-6 求区间和

本题会给你一段长度为N的整数序列,并进行K次询问。每次询问要求你给出区间a到b的和(序列下标由1到N)。由于区间和可能较大,请你输出它对10000019取模的结果。

输入格式:
首先一行整数N,代表序列长度。
接下来一行N个整数,为序列内容。
接下来一行整数K,代表对区间和的询问次数。
接下来K行,每行两个整数a和b,请你输入序列的第a到b号元素(含)的和取模后的结果。

输出格式:
一共K行,每行一个整数,代表询问的区间和取模后的结果。

输入样例:

5
1 2 3 4 5
3
1 5
2 3
3 3

输出样例:

15
5
3

数据限制:
N<=1e​6,K<=1e5
​​
使用前缀和数组,经过预处理后,每次查询只需要O(1)的时间复杂度。
(由于输入的数据量过大,用cin,cout有时候会超时)
即对于一个数字序列。用arr[i]数组储存这串数的前 i 项的和,那么每次寻找序列第a到b号元素的和时就不需要遍历a到b之前的所有数了,只需要计算arr[b] - arr[a-1]的值就行。
比如序列:3 2 4 1 5 6。
arr数组(以1位数组下标)对应的值是:3 5 9 10 15 21。
计算序列第3项到第5项的值就是 arr[5]-arr[2] = 15-5 = 10。
前缀和算法实用性很高,此外还有差分算法,建议不了解的同学在网上看看相关文章。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
const int mod = 10000019;
int arr[N];
int main(){
    
    
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n, a, b, m;
	cin >> n;
	for(int i = 1; i<=n; i++){
    
    
		cin >> a;
		arr[i] = (arr[i-1]+a)%mod;
	}
	cin >> m;
	while(m--){
    
    
		cin >> a >> b;
		cout << (arr[b]+mod-arr[a-1])%mod << endl;
		// +mod 是为了arr[b]<arr[a]的情况
	}
}

7-7 最大子段和

给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。

输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。

输出格式:
输出最大子段和。

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

动态规划的入门题。
设dp[i]为以第i个元素结尾且和最大的连续子数组。
假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素。
dp[i] = max(dp[i-1] + a[i], a[i])。可以通过判断sum[i-1] + a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4+10;
int dp[N];
int main(){
    
    
	int n, ans = 0, a;
	cin >> n;
	for(int i=1; i<=n; i++) {
    
    
		cin >> a;
		dp[i] = max(dp[i-1]+a, a);
		ans = max(dp[i], ans);
	}
	cout << ans;
}

此外由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,即不需要数组储存之前的结果,只需要保留上一次的即可。

7-8 逆波兰表达式求值

逆波兰表示法是一种将运算符(operator)写在操作数(operand)后面的描述程序(算式)的方法。举个例子,我们平常用中缀表示法描述的算式(1 + 2)*(5 + 4),改为逆波兰表示法之后则是1 2 + 5 4 + *。相较于中缀表示法,逆波兰表示法的优势在于不需要括号。
请输出以逆波兰表示法输入的算式的计算结果。

输入格式:
在一行中输入1个算式。相邻的符号(操作数或运算符)用1个空格隔开。

输出格式:
在一行中输出计算结果。

限制:
2≤算式中操作数的总数≤100
1≤算式中运算符的总数≤99
运算符仅包括“+”、“-”、“*”,操作数、计算过程中的值以及最终的计算结果均在int范围内。

输入样例1:

4 3 + 2 -

输出样例1:

5

输入样例2:

1 2 + 3 4 - *

输出样例2:

-3

考察数据结构中的应用。
对于每次输入的元素。如果当前元素是整数,则压入栈;如果是运算符,则将栈顶两个元素弹出做相应运算,再将结果入栈。
最终表达式扫描完后,栈里的数就是结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
stack<int> arr;

//将字符串转为数字
int to_int(string str){
    
    
	int ans = 0, i = 0, f = 0;
	if(str[0] == '-') {
    
    
		f = 1; i = 1;
	}
	for(;i<str.size(); i++){
    
    
		ans = ans*10+str[i]-'0';
	}
	if(f) ans = 0-ans;
	return ans;
}

int main(){
    
    
	string str;
	while(cin >> str){
    
    
		if(str[0] == '+'||str[0] == '-'||str[0] == '*'){
    
    
			int n2 = arr.top(); arr.pop();
			int n1 = arr.top(); arr.pop();
			if(str[0] == '+') arr.push(n1+n2);
			else if(str[0] == '-') arr.push(n1-n2);
			else arr.push(n1*n2);
		}else{
    
    
			arr.push(to_int(str));
		}
	}
	cout << arr.top();
}

7-9 排排排名

众所周知,Keven所在的集训队经常举办比赛,然而每次比赛的排名统计是一个大麻烦,于是Keven找到了你并给了你这次比赛的分数,希望你能帮他统计一下这次比赛前K名队员的排名。

输入格式:
输入在第一行给出 2 个整数,分别是 N(不超过 10 000 的正整数,为学生总数)、K(不超过 100 且不超过 N 的正整数)。接下来 N 行,每行给出一位学生的账号(长度不超过15位、不带空格的字符串)和比赛成绩(区间 [0, 100] 内的整数),其间以空格分隔。题目保证没有重复的账号。

输出格式:
然后按总评成绩非升序输出前K名学生的名次、账号和成绩,其间以 1 个空格分隔。需要注意的是:成绩相同的学生享有并列的排名,排名并列时,按账号的字母序升序输出。
需要注意的是:
成绩相同的学生享有并列的排名,排名并列时,按账号的字母序升序输出。详见第二个样例。
假设现在有三位同学,他们的成绩分别是100、100、80,那么此时两位100分的同学的排名应该都是 1 ,并且80分的同学的排名是 3 。

输入样例1:

10 5
[email protected] 78
[email protected] 87
[email protected] 65
[email protected] 96
[email protected] 39
[email protected] 87
[email protected] 80
[email protected] 88
[email protected] 80
[email protected] 70

输出样例1:

1 [email protected] 96
2 [email protected] 88
3 [email protected] 87
3 [email protected] 87
5 [email protected] 80
5 [email protected] 80

直接根据分数与账号的字典序排序就行。输出的时候注意名次可以相等,需要用变量记录上一个人的分数和相等的名次数量,后面再加上。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4+10;
struct node{
    
    
	string str;
	int n;
}arr[N];

bool cmp(node a, node b){
    
    
	if(a.n == b.n){
    
    
		return a.str < b.str;
	}
	return a.n > b.n;
}
int main(){
    
    
	int n, m, f = 0, last = -1, lastn = 1; 
	cin >> n >> m;
	for(int i = 0; i < n; i++) cin >> arr[i].str >> arr[i].n;
	sort(arr,arr+n,cmp);
	// f:是当前的名次, last:是上一个人的分数, lastn是有几个拥有last分数的人
	for(int i = 0; i < n; i++){
    
    
		if(arr[i].n!=last){
    
    
			last = arr[i].n;
			f += lastn; lastn = 1;
		}else lastn++;
        if(f>m) break;
		cout << f <<" "<< arr[i].str << " " << arr[i].n << endl;
	}
}

7-10 白骑士的移动

小S第一次接触国际象棋。他发现国际象棋中的Knight棋子的移动方式和中国象棋中的马类似。
于是小S在棋盘上随意摆上了一些棋子,其中包括一枚白骑士、一枚黑皇后、若干黑战车和若干黑主教。
小S想知道,如何能在避开黑战车和黑主教的攻击范围的前提下,花费更少的步数吃掉黑皇后。
注1:战车的攻击范围呈直线,和中国象棋的車类似;主教的攻击范围呈斜线,无障碍物情况下可无限延伸。
注2:白骑士只能吃黑皇后,不可以吃掉黑战车和黑主教。

输入格式:
输入仅包含一组样例。
一组样例包含8行(分别对应1-8行),每行包含8个字符,每个字符代表对应行对应列的棋盘格子状况。
其中’ . ‘代表格子上没有摆放任何棋子;’ K '代表格子上摆放的是白骑士; ’ Q '代表格子上摆放的是黑皇后; ’ R '代表格子上摆放的是黑战车; ’ B '代表格子上摆放的是黑主教。
注:题目保证白骑士的初始位置不在黑战车和黑主教的攻击范围内。

输出格式:
如果白骑士可以在避开黑战车和黑主教的攻击的情况下吃掉黑皇后,则输出花费步数的最小值;否则输出"Checkmate"。

输入样例1:

R.B.QB.R
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. K . . . . . .

输出样例1:
4

跟迷宫题类似,不过马的路径跟走迷宫的不太一样,需要注意。寻找最小的步数,所以要用bfs(本题中dfs应该也不会超时),注意黑战车与黑主教的攻击,遇到障碍后就会停止。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

char arr[15][15];
int vis[15][15], ans = 0, bx, by;
// 马的路径
int d[8] = {
    
    2,2,1,1,-1,-1,-2,-2};
int f[8] = {
    
    1,-1,2,-2,2,-2,1,-1};

struct node{
    
    
	int x, y, v;
};
//黑主教的攻击范围
void disB(int x, int y){
    
    
	vis[x][y] = 1;
	for(int i=x+1,j=y+1; i<=8&&j<=8; i++,j++){
    
    
		if(arr[i][j] != '.') break;
		vis[i][j] = 1;
	}
	for(int i = x-1, j = y-1; i>=1&&j>=1; i--,j--){
    
    
		if(arr[i][j] != '.') break;
		vis[i][j] = 1;	
	}
	for(int i=x+1,j=y-1; i<=8&&j>=1; i++,j--){
    
    
		if(arr[i][j] != '.') break;
		vis[i][j] = 1;	
	}
	for(int i=x-1,j=y+1; i>=1&&j<=8; i--,j++){
    
    
		if(arr[i][j] != '.') break;
		vis[i][j] = 1;	
	}
}
//黑战车的攻击范围
void disR(int x, int y){
    
    
	vis[x][y] = 1;
	for(int i=x+1; i <= 8;i++){
    
    
		if(arr[i][y] != '.') break;
		vis[i][y] = 1;
	}
	for(int i=x-1; i >= 1;i--){
    
    
		if(arr[i][y] != '.') break;
		vis[i][y] = 1;
	}
	for(int i=y+1; i<= 8;i++){
    
    
		if(arr[x][i] != '.') break;
		vis[x][i] = 1;
	}
	for(int i=y-1; i >= 1;i--){
    
    
		if(arr[x][i] != '.') break;
		vis[x][i] = 1;
	}
}

void bfs(){
    
    
	queue<node> q;
	node b; b.x=bx; b.y=by; b.v = 0;
	q.push(b);
	while(!q.empty()){
    
    
		node p = q.front(); q.pop();
		for(int i = 0; i<8; i++){
    
    
			int x = p.x+d[i];
			int y = p.y+f[i];
			if(arr[x][y] == 'Q'){
    
    
				ans = p.v+1; return;
			}
			if(x>=1 && x<=8 && y>=1 && y<=8 && vis[x][y]==0){
    
    
			//	cout << x <<" "<< y << endl;
				vis[x][y] = 1;
				node n; n.x = x; n.y = y; n.v =p.v+1;
				q.push(n);
			}
		
		}
	}
}
//对棋盘中的棋子进行处理
void check(){
    
    
	for(int i = 1; i<=8; i++){
    
    
		for(int j = 1; j<= 8; j++){
    
    
			if(arr[i][j] == 'R'){
    
    
				disR(i,j);
			}else if(arr[i][j] == 'B'){
    
    
				disB(i,j);
			}else if(arr[i][j] == 'K'){
    
    
				bx = i; by = j;
			}
		}
	}
}

int main(){
    
    
	for(int i = 1; i<=8; i++){
    
    
		for(int j = 1; j<= 8; j++){
    
    
			cin >> arr[i][j];
		}
	}
	check();
	bfs();
	if(ans == 0) cout << "Checkmate";
	else cout << ans;
	
}

猜你喜欢

转载自blog.csdn.net/qq_45874814/article/details/115426669