2019第十届蓝桥杯大赛青少年创意编程省赛C++组试题解析

水平有限,如有不当请不吝赐教,万分感谢

  1. 水下探测器
    水下探测器可以潜入湖中在任意水深进行科学探索。
    湖水的最大深度为 h 米,即它在湖底时到水面的距离,0<=h<=100;
    探测器最初的水下深度为 s 米,0<=s<=100;
    当探测器不在水面(当前深度大于 0)时,每个 u 指令可使它上浮 1 米,而当探测器在水面时,u 指令是无效的;
    当探测器不在湖底(当前深度小于 h)时,每个 d 指令可使它下沉 1 米,而当探测器在湖底时,d 指令是无效的;
    在执行到无效指令时,探测器不做任何操作而继续执行下一指令。
    编程实现:
    根据给定的 h、s 和一个指令序列(由字符 u、d 组成的字符串,长度不超过 100),求出执行完整的指令序列后,探测器的水下深度。
    输入:
    第一行:h 和 s,以空格分开。0<=s<=h<=100
    第二行:长度不超过 100 的指令字符串,串中仅包含字母 u 或 d
    输出:
    代表探测器在执行指令后的水下深度的数字。
    样例输入:
    9 1
    uduudd
    样例输出:
    2

样例数据分析:
水深9米,探测器在水下1米处,

字符u代表向上1米,探测器上浮到0米处

字符d代表向下1米,探测器下沉到1米处

字符u代表向上1米,探测器上浮到0米处

字符u代表向上1米,探测器已经在水面,不能上浮,依然在0米处

字符d代表向下1米,探测器下沉到1米处

字符d代表向下1米,探测器下沉到2米处

最终结果为2

考察知识:
基础语法,字符串,循环,条件判断

参考代码:

#include <iostream>
using namespace std;

int main(){
	int h,s;
	string str;
	cin>>h>>s>>str;
	for(int i=0;i<str.size();i++){
		if(str[i]=='u'){
			if(s>0){
				s--;
			}
		}else{
			if(s<h){
				s++;
			}
		}
	}
	cout<<s;
}

//蓝桥杯组委会:16601195122 田老师
//作者:17704313221 宫老师
//[易码编程](http://www.521ma.cn)
//[练习系统](http://oj.521ma.cn)
  1. 猫吃鱼
    明明家从 1 号站点出发,开车去旅游,一共要经过 n 个站点,依次为 2、3……n。
    由于明明带上了心爱的小猫,在每个站点都要为小猫提供一条鱼用做美餐(包括 1 号站点)。
    除了 1 号站点只能吃 1 号站点买的鱼,其他站点既可以吃当地买的鱼,也可吃之前经过的站点买了存入车载冰箱中的鱼。
    但车载冰箱消耗的电能来自汽油,所以每条鱼用冰箱保存到下一站的费用与各个站点的汽油价格有关。
    为使问题简化,我们约定:
    (1)车从某站开出时油箱中都是此站点刚加的汽油。
    (2)车载冰箱能容纳一路上需要的所有鱼。
    即:每条鱼的费用既包括购买时的费用,也包括用冰箱保存鱼的费用。
    编程实现:
    为了降低小猫吃鱼的总代价,明明预先上网查到了这 n 个站点的鱼价和汽油价格。并据此算出每个站点买一条鱼的费用以及从该站点到下一站用冰箱保存一条鱼的费用。你能帮明明算出这一路上小猫吃鱼的最小总费用吗?
    输入:
    第一行:站点数 n,1<n<100。
    接下来的 n 行:每行两个以空格分隔的正整数,表示:这一站买一条鱼的费用,以及从这一站把每条
    鱼保存到下一站的费用,两个费用均为小于 10000 的正整数。
    输出:
    最小总费用,是一个正整数。
    样例输入:
    5
    6 3
    7 1
    3 2
    8 3
    9 5
    样例输出:
    29

样例数据分析:
第一行数据 5 ,代表一共5站

第二行数据6 3 代表本站购买鱼6元,运费3元,第一站必须一定先购买一条 总花费6元

第三行数据7 1 代表本站购买鱼7元,运费1元,从上一站最小花费+运费9元,大于本站购买的费用7,所以选择从本站购买鱼,总花费 6+7=13元

第四行数据3 2 代表本站购买鱼3元,运费2元,从上一站最小花费+运费8元,大于本站购买的费用3,所以选择从本站购买鱼,总花费 6+7+3=16元

第五行数据8 3 代表本站购买鱼8元,运费3元,从上一站最小花费+运费5元,小于本站购买的费用8,所以选择从上一站花费加上本站运费,总花费6+7+3+5=21元

第六行数据9 5 代表本站购买鱼9元 运费5元,从上一站最小花费+运费8元,小于本站购买的费用9,所以选择从上一站花费加上本站运费,总花费6+7+3+5+8=29元

最终总花费为29元

考察知识:
数组,递推算法

参考代码:

#include <iostream>
using namespace std;
int main(){
	int n;
	cin>>n;
	int a[n],b[n];
	for(int i=0;i<n;i++){
		cin>>a[i]>>b[i];
	}
	int min=99999,t=0;
	for(int i=0;i<n;i++){
		if(min>a[i]){
			min=a[i];
		}
		t=t+min;
		min=min+b[i];
	}
	cout<<t;
}
//蓝桥杯组委会:16601195122 田老师
//作者:17704313221 宫老师
//[易码编程](http://www.521ma.cn)
//[练习系统](http://oj.521ma.cn)
  1. 评选最佳品牌
    n 个评委投票,在 m 个商品中评选一个最佳品牌。
    评选采用多轮淘汰制,即:每轮投票,淘汰掉得票最少的候选品牌(得票并列最少的品牌一起淘汰)。
    如此一轮轮淘汰下去,如果最后只剩下一个品牌当选,即告评选成功。
    但如果在某轮投票中,当时未被淘汰的所有候选品牌(大于等于两个品牌)都并列得票最少,即告评选失败。
    如果评选成功就输出当选品牌号。否则输出最后一轮评选时唯一选票数的相反数。
    在评选流程中,每个评委的态度都可用一个序列来表示;例如当 m=5 时,某评委的评选态度序列为:3、
    5、1、2、4,则表示该评委:优先投 3 号,当 3 号被淘汰时投 5 号,当 3 和 5 都被淘汰时投 1,当 3、5、1 都被淘汰时投 2,仅剩 4 号时才投 4 号品牌的票。
    选票的序列中可以表示弃权,用 0 来表示,例如当 m=5 时,某评委的评选态度序列为:3、5、0,则表示该评委:优先投 3 号,当 3 号被淘汰时投 5 号,其它情况下不投任何品牌的票。
    编程实现:
    请你编一个程序,模拟各轮投票的过程,得到评选结果。
    输入:
    第一行:m(0<m<10,表示参加评选的品牌数)和 N(1<n<1000,表示参加投票的评委数),之间以空格分隔接下来的 n 行:每行都是长度不超 m 的数字字符串,每个字符串表示一个评委的评选态度。
    输出:
    评选结果。
    样例 1 输入:
    3 4
    123
    213
    132
    10
    样例 1 输出:
    1
    样例 2 输入:
    3 4
    321
    213
    231
    312
    样例 2 输出:
    -2
    样例数据分析:
  • 样例1:
    第一行3 4 代表3个品牌,4个评委

第一轮投票,3个评委优先选择1号品牌,1个评委选择2号品牌,品牌3得票最少,淘汰掉

第二轮投票,3个评委优先选择1号品牌,1个评委选择2号品牌,品牌2得票最少,淘汰掉,淘汰2号品牌后,只剩一个1号品牌,1号品牌胜出

最终结果 1

  • 样例2:
    第一行3 4 代表3个品牌,4个评委

第一轮投票,2个评委选择2号品牌,两个评委选择3号品牌,1号得票最少,淘汰掉

第二轮投票,2个评委选择2号品牌,两个评委选择3号品牌,由于只剩下两个品牌,且并列最少,都是2票,代表评选失败,需要输出最后一轮票数2的相反数-2

最终结果 -2

考察知识:
字符串,桶排序,模拟算法

参考代码:

#include <iostream>
using namespace std;

int main(){
	int m,n;
	cin>>m>>n;
	string str[n+1];
	for(int i=1;i<=n;i++){
		cin>>str[i];
	}
	int a[m+1];//记录投票过程,-1代表淘汰
	while(true){
		for(int i=1;i<=m;i++){
			//重新计票,所有没有被淘汰的票数归零
			if(a[i]!=-1)a[i]=0;
		}
		for(int i=1;i<=n;i++){
			//计票
			string s=str[i];
			for(int j=0;j<s.size();j++){
				char t=s[j]-'0';
				if(t==0){
					//如果为0 说明弃权
					break;
				}else{
					//不为0,则代表要给t投票,如果t没有被淘汰则给它投票
					if(a[t]>=0){
						a[t]=a[t]+1;
						break;
					}
				}
			}
		}
		
		int min=m+1;
		int max=0;
		for(int i=1;i<=m;i++){
			if(a[i]>=0){
				if(min>a[i]) min=a[i];
				if(max<a[i])max=a[i];
			}
		}
		
		if(max>min){
			//最大值比最小值大,需要将所有最小值的淘汰
			for(int i=1;i<=m;i++){
				if(a[i]==min)a[i]=-1;
			}
		}else{
			//最大值和最小值相等说明平票
			int count=0;
			int dx=0;
			for(int i=1;i<=m;i++){			
				if(a[i]==max){
					count++;
					dx=i;
				}
			}
			if(count>1){
				//如果数量多,说明评选失败
				cout<<0-max;
			}else{
				cout<<dx;
			}
			break;
		}
	}
	cin>>m;
}
//蓝桥杯组委会:16601195122 田老师
//作者:17704313221 宫老师
//[易码编程](http://www.521ma.cn)
//[练习系统](http://oj.521ma.cn)
  1. 最大购物优惠
    小惠听说超市正在打折促销,要制订一个得到最大优惠的购物计划。
    小惠的体力可以提起 w 单位重量的东西,还有一个能装 V 个单位体积的购物袋,并详细了解了各打折商品的重量、体积及此商品实际优惠的金额。她想在自己体力的限度和购物袋容积限度内,尽可能多地得到购物优惠。
    超市规定这些打折商品每种只能购买一件。
    编程实现:
    请你编写程序,制定一个购买商品的计划,求出小惠能得到的最大优惠金额和实际应购买的各商品序号。
    输入:
    第一行:依次为 w、v 和 n(n 为商品种类数),所有数值均为不超过 100 的正整数
    接下来的 n 行:每行有三个整数,依次为某种商品的重量、体积和让利金额,数值间以空格分开,所有数值均为不超过 100 的正整数
    输出:
    第一行:小惠能够得到的最大让利金额
    第二行:依次为从小到大排列的商品序号,序号从 1 开始,序号间用空格分开。若第二行输出的序列不唯一,则输出其最小字典序。
    样例输入:
    10 9 4
    8 3 6
    5 4 5
    3 7 7
    4 5 4
    样例输出:
    9
    2 4

样例数据分析:
第一行数据 10 9 4,代表最多能提起重量10,购物袋体积9,4种商品

第二行数据代表 第1件商品重量8 ,体积3,让利金额4

第三行数据代表 第2件商品重量5 ,体积4,让利金额5

第四行数据代表 第3件商品重量3 ,体积7,让利金额7

第五行数据代表 第4件商品重量4 ,体积5,让利金额4

创建一个三维数组 yh[n+1][w+1][v+1] 记录在第n个物品,w重量,v体积时 最优策略

只考虑一个物品的时候(纵向代表重量,横向代表体积,数据代表最大优惠)
在这里插入图片描述
考虑前两个物品的时候
在这里插入图片描述
考虑前三个物品的时候
在这里插入图片描述
考虑前四个物品的时候
在这里插入图片描述
最终结果,最大优惠为9,同时需要额外记录在在不同商品数量,不同重量和体积的情况下,如何购买商品

考察知识:
动态规划,二维费用的背包问题,在01背包问题的基础上增加一个费用维度

状态转移方程:

yh[i][j][k]=max(yh[i-1][j][k],yh[i-1][j-w[i]][k-v[i]]+n[i])

经典01背包问题解(https://blog.csdn.net/qq_38410730/article/details/81667885)

参考代码:

#include <iostream>
using namespace std;
int main(){
	int w,v,n;
	cin>>w>>v>>n;
	int a[n+1],b[n+1],c[n+1];
	int yh[n+1][w+1][v+1];
	string s[n+1][w+1][v+1];
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i]>>c[i];
		
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<=w;j++){
			for(int k=0;k<=v;k++){
				yh[i][j][k]=0;
				s[i][j][k]="";
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=w;j++){
			for(int k=1;k<=v;k++){
				int bn=yh[i-1][j][k];
				int n=0;
				if(j>=a[i]&&k>=b[i]){//重量和体积够的时候,尝试拿当前物品
					n=yh[i-1][j-a[i]][k-b[i]]+c[i];
				}
				if(n>bn){//如果拿了比不拿价值大,就拿这个物品
					yh[i][j][k]=n;
					//同时记录拿了这个物品
					s[i][j][k]=s[i-1][j-a[i]][k-b[i]]+" "+(char)(i+'0');
				}else{
				    //否则记录当前重量和体积的最优策略是不拿
					yh[i][j][k]=bn;
					//同时记录不拿当前物品,保持和之前一样的物品列表
					s[i][j][k]=s[i-1][j][k];
				}
				//printf("%d %d %d %d %d ",i,j,k,n,bn); 
				//cout<<s[i][j][k]<<endl;
			}
		}
		//cin>>a[0];
	}
	cout<<yh[n][w][v]<<endl;
	string str=s[n][w][v];
	cout<<str.substr(1,str.size()-1);

}
//蓝桥杯组委会:16601195122 田老师
//作者:17704313221 宫老师
//[易码编程](http://www.521ma.cn)
//[练习系统](http://oj.521ma.cn)
  1. 蓝桥杯赛迷宫
    把一个 n 行 m 列的字符阵列看做一个迷宫,迷宫仅包含 L、Q、B、S 中的大写字母(蓝桥杯赛的汉语拼音首字母)。
    初始时,你可以从任意一个“L”字母开始,移向相邻的“Q”字母,然后从此“Q”字母出发,移向相邻的“B”字母,然后从此“B”字母出发,移向相邻的“S”字母……。这样,你就算是走过了一个“LQBS”字符序列。
    接下来,仍然可以从此“S”字母出发,移向相邻的“L”字母……,重复上述的动作,你就可以不断地走过“LQBS”序列。
    请注意,所谓相邻仅包含上、下、左、右 4 个方向,且只能从 L->Q,从 Q->B,从 B->S,从 S->L。
    可以想像,由于选择的出发点不同,我们有可能在迷宫中走过无数次的“LQBS”,或者是有限次的“LQBS”,
    或者一次也走不了。
    编程实现:
    请你编写程序,求出在给定的迷宫中,我们最多可以走过多少次“LQBS”?
    输入:
    第一行:正整数 n,m,表示迷宫的规模为 n 行 m 列,0<m<100,0<n<100
    接下来的 n 行:每行 m 个符合题意的字母,字母间无空格。
    输出:
    一个整数。即:如果在迷宫中可以无限次的走过“LQBS”,输出-1,否则,输出可以走过“LQBS”的最多次数。
    样例 1 输入:
    1 2
    LQ
    样例 1 输出:
    0
    样例 2 输入:
    3 3
    LSB
    QBQ
    BSL
    样例 2 输出:
    -1
    样例 3 输入:
    4 4
    BLQB
    BBQS
    SBQL
    QQQQ
    样例 3 输出:
    2

样例数据分析:

  • 样例1:
    第一行数据3 3 代表是3行3列的迷宫

第1步,寻找L
LSB
QBQ
BSL

第2步,在L的上下左右寻找Q
LSB
QBQ
BSL

第3步,在Q的上下左右寻找B
LSB
QBQ
BSL

第4步,在B的上下左右寻找S
LSB
QBQ
BSL

如此当重复到10次的时候,仍可以继续走,代表进入无限循环

因为3行3列最多9个格子,能走10步就代表一定进入无限循环

输出-1

  • 样例2:
    第一行数据4 4 代表是4行4列的迷宫

第1步,寻找起点L
BLQB
BBQS
SBQL
QQQQ

第2步,在L的上下左右寻找Q
BLQB
BBQS
SBQL
QQQQ

第3步,在Q的上下左右寻找B
BLQB
BBQS
SBQL
QQQQ

第4步,在B的上下左右寻找S
BLQB
BBQS
SBQL
QQQQ

第5步,在S的上下左右寻找L
BLQB
BBQS
SBQL
QQQQ

第6步,在L的上下左右寻找Q
BLQB
BBQS
SBQL
QQQQ

第7步,在Q的上下左右寻找B
BLQB
BBQS
SBQL
QQQQ

第8步,在B的上下左右寻找S
BLQB
BBQS
SBQL
QQQQ

第9步,在S的上下左右寻找L

因为找不到L了,则代表迷宫走到尽头,一共走了8步,LQBS一共4个字符,所以走了
8/4=2共计2次LQBS,输出2

考察知识:
搜索与回朔
由于笔者的学员暂时未学到搜索与回朔算法,所以采用循环遍历类似穷举算法进行题解

参考代码:

#include<iostream>
using namespace std;
int main(){
	int n,m;
	cin>>n>>m;
	char map[n+1][m+1];
	int path[n+1][m+1];
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		for(int j=1;j<=m;j++){
			map[i][j]=s[j-1];
			path[i][j]=0;
			if(map[i][j]=='L'){
				path[i][j]=1;
			}
		}
	}
	string LQBS="LQBS";
	
	int now=1;
	while(true){	
		int flag=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(path[i][j]==now){
					if(map[i][j-1]==LQBS[now%4]){
						path[i][j-1]=now+1;
						flag++;
					}
					if(map[i][j+1]==LQBS[now%4]){
						path[i][j+1]=now+1;
						flag++;
					}
					if(map[i-1][j]==LQBS[now%4]){
						path[i-1][j]=now+1;
						flag++;
					}
					if(map[i+1][j]==LQBS[now%4]){
						path[i+1][j]=now+1;
						flag++;
					}				
				}
			}			
		}
		/*for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cout<<path[i][j]<<" ";
			}
			cout<<endl;
		}
		cout<<endl;*/
		if(flag==0){
			break;
		}
		if(now>(n+1)*(m+1)){
			break;
		}
		now++;
	}
	if(now>(n+1)*(m+1)){
		cout<<-1;
	}else{
		cout<<now/4;
	}
	cin>>n;
}
//蓝桥杯组委会:16601195122 田老师
//作者:17704313221 宫老师
//[易码编程](http://www.521ma.cn)
//[练习系统](http://oj.521ma.cn)
  • 15
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值