前言
这套题中涉及到的知识点有 模拟、前缀和、贪心、快速排序、动态规划、结构体、栈、队列、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重复这种操作,可以产生新自然数N2 ;……多次重复这种操作,运算结果最终会得到一个固定不变的数Nk,就像掉入一个数字“陷阱”。
本题要求对输入的自然数,给出其掉入“陷阱”的过程。
输入格式:
在一行内给出一个自然数N0(N0 < 30000)。
输出格式:
对于输入的N0,逐行输出其掉入陷阱的步骤。第i行描述N掉入陷阱的第i步,格式为: i:Ni(i≥1)。当某一步得到的自然数结果Nk(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<=1e6,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;
}