2019年第十届蓝桥杯省赛Java A组真题解析(带源码)

第一题:平方和(填空题)
题目描述
小明对数位中含有 2、0、1、9 的数字很感兴趣,在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574,平方和是 14362。注意,平方和是指将每个数分别平方后求和。
请问,在 1 到 2019 中,所有这样的数的平方和是多少?

分析:无

package A组2019年;



public class _1平方和 {
	public static void main(String[] args) {
		long ans = 0;
		for(int i=1;i<=2019;i++) {
			if(check(i)) {
				ans += Math.pow(i, 2);
			}
		}
		System.out.println(ans);
	}
	
	static boolean check(int i) {
		String s = String.valueOf(i);
			for(int j=0;j<s.length();j++) {
				if(s.charAt(j) == '2' || s.charAt(j) == '0' ||s.charAt(j) == '1' ||s.charAt(j) == '9') {
					return true;
				}
			}
			return false;
	}

}

第二题:数列求值(填空题)

题目描述
给定数列 1, 1, 1, 3, 5, 9, 17, …,从第 4 项开始,每项都是前3 项的和 。求
第 20190324 项的最后 4 位数字。

分析:暴力枚举,每项都是前3项的和,和对10000取余,因为只要最后四位数字。

package A组2019年;



public class _2数列求值 {
	public static void main(String[] args) {
		long[] ans = {1,1,1};
		for(int i=4;i<=20190324;i++) {
			long now = ans[0] + ans[1] + ans[2];
			ans[0] = ans[1];
			ans[1] = ans[2];
			ans[2] = now%10000;
		}
		
		
		System.out.println(ans[2]);
	}
}

第三题:最大降雨量(填空题)
题目描述
由于沙之国长年干旱,法师小明准备施展自己的一个神秘法术来求雨。
这个法术需要用到他手中的 49 张法术符,上面分别写着 1 至 49 这 49 个
数字。法术一共持续 7 周,每天小明都要使用一张法术符,法术符不能重复使用。
每周,小明施展法术产生的能量为这周 7 张法术符上数字的中位数。法术
施展完 7 周后,求雨将获得成功,降雨量为 7 周能量的中位数。
由于干旱太久,小明希望这次求雨的降雨量尽可能大,请大最大值是多少?

分析:一个小学脑筋急转弯,详细见代码

package A组2019年;

public class _最大降雨量 {
	public static void main(String[] args) {
		 /*
         * [][][][a][][][]
         * [][][][b][][][]
         * [][][][c][][][]
         * [][][][max][][][]
         * [][][][d][][][]
         * [][][][e][][][]
         * [][][][f][][][]
         *
         * 此题意思为将49分为7组数字,求取七组数字中每组数字的中位数所构成的数列的中位数的最大值
         * 即如图所示,最大化[max]
         * 49个数字中需要比[max]大的有【max】行的后三位,d、e、f行的后四位
         * 即结果如下
         * */
        System.out.println(49 - (3 * 4) - 3);
	}
}

第四题:迷宫(填空题)
题目描述
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。

010000
000100
001001
110000

迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。请注意在字典序中D<L<R<U。(如果你把以下文字复制到文本文件中,请务 必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 maze.txt, 内容与下面的文本相同)

01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个字符串,包含四种字母 D、U、L、R,在提交答案时只填写这个字符串,填写多余的内容将无法得分。

分析:迷宫题一般都是BFS搜索找最短路,这题是迷宫变形,需要按照字典最小序走找出最短路并且输出。

1.我们按照正常的BFS走迷宫进行广度遍历,创建一个char[][]数组存放走的方向(经典迷宫是创建in[][]储存走的距离),遍历到某一个点的时候,按照D<L<R<U的顺序遍历(字典序从小到大),将每个位置从哪个方向来的记录下来,例如direction[1][0] = 'D',它是从direction[0][0]下来的;

2.BFS遍历完迷宫后开始找怎么走:按照遍历的方向从起点走到终点有很多条路,但是从终点走回到起点按照遍历的方向走只有唯一一条路,因此从终点开始走,然后将字符串反转,就是答案

3.不能用dfs爆搜,会爆栈(栈过深)

package A组2019年;

import java.util.LinkedList;
import java.util.Queue;

//思路:先BFS找到最少需要多少步能走到终点
//然后:从终点BFS(按照字典序最小)回到起点,记录方向
//AC
public class _5迷宫 {
    private static String[] nn= {
            "01010101001011001001010110010110100100001000101010",
            "00001000100000101010010000100000001001100110100101",
            "01111011010010001000001101001011100011000000010000",
            "01000000001010100011010000101000001010101011001011",
            "00011111000000101000010010100010100000101100000000",
            "11001000110101000010101100011010011010101011110111",
            "00011011010101001001001010000001000101001110000000",
            "10100000101000100110101010111110011000010000111010",
            "00111000001010100001100010000001000101001100001001",
            "11000110100001110010001001010101010101010001101000",
            "00010000100100000101001010101110100010101010000101",
            "11100100101001001000010000010101010100100100010100",
            "00000010000000101011001111010001100000101010100011",
            "10101010011100001000011000010110011110110100001000",
            "10101010100001101010100101000010100000111011101001",
            "10000000101100010000101100101101001011100000000100",
            "10101001000000010100100001000100000100011110101001",
            "00101001010101101001010100011010101101110000110101",
            "11001010000100001100000010100101000001000111000010",
            "00001000110000110101101000000100101001001000011101",
            "10100101000101000000001110110010110101101010100001",
            "00101000010000110101010000100010001001000100010101",
            "10100001000110010001000010101001010101011111010010",
            "00000100101000000110010100101001000001000000000010",
            "11010000001001110111001001000011101001011011101000",
            "00000110100010001000100000001000011101000000110011",
            "10101000101000100010001111100010101001010000001000",
            "10000010100101001010110000000100101010001011101000",
            "00111100001000010000000110111000000001000000001011",
            "10000001100111010111010001000110111010101101111000"};
	public static void main(String[] args) {
		int[][] mg = new int[nn.length][nn[0].length()];
		for(int i=0;i<nn.length;i++) {
			for(int j=0;j<nn[i].length();j++) {
				mg[i][j] = Integer.valueOf(String.valueOf(nn[i].charAt(j)));
			}
		}
		//广度遍历找出最短路径
		char[][] ans = bfs(0,0,mg);
		
//		System.out.println(ans[ans.length-1][ans[0].length-1]);
//		从起点到终点有无数中走法,但是从终点到起点就一种走法
		int x = ans.length-1;
		int y = ans[0].length-1;
		StringBuffer s = new StringBuffer();
		while(!(x==0&&y==0)) {//从终点出发,回到原点
			char temp = ans[x][y];
			s.append(temp);
//			D、U、L、R 分别表示向下、向上、向左、向右
			if(temp=='D')
                x --;
            if(temp=='L')
                y++;
            if(temp=='R')
                y--;
            if(temp=='U')
                x++;
		}
		
		//将字符串反转后输出
		System.out.println(s.reverse());
	
//		StringBuffer s = dfs(nn.length-1,nn[0].length()-1,sbuff,mg);
//		System.out.println(s);
		
	}
	/*
	 * static StringBuffer dfs(int x,int y,StringBuffer s,int[][] mg) { //1.递归先写出口
	 * if(x == 0 && y == 0)return s; //判断怎么走D下<L左<R右<U上 //由于我们从终点走到起点,所以要倒着走 U R L D
	 * // D、U、L、R 分别表示向下、向上、向左、向右走 int[] dx = {0,1,-1,}; int[] dy = {-1,0,0,1};
	 * for(int i=0;i<4;i++) { int nowx = x + dx[i]; int nowy = y + dy[i]; //2.判断没有越界
	 * if(nowx < 0 || nowx>= mg.length || nowy<0 || nowy>=mg[0].length) { continue;
	 * } if(mg[nowx][nowy] == 2) {//说明此路可以走 StringBuffer[] zxc = {new
	 * StringBuffer("U"),new StringBuffer("R"),new StringBuffer("L"),new
	 * StringBuffer("D")}; s.append(zxc[i]); return dfs(nowx,nowy,s,mg); } } return
	 * null;
	 * 
	 * }
	 */
	
	static char[][] bfs(int x,int y,int[][] mg) {
		//1.新建一个数组来存放走来的方向
		char[][] boo = new char[mg.length][mg[0].length];
		//2.创建一个队列,用来广度遍历迷宫
		Queue<wz> queue = new LinkedList<wz>();
		//3.将当前位置加入到队列中
		queue.add(new wz(x,y));
		//4.将初始点标记为已走过
		mg[x][y] = 2;
		//5.判断队列是否为空
		while(!queue.isEmpty()) {
			wz now = queue.poll();
//			D<L<R<U 下左右上,按照字典顺序最小走
			int[] dy = {0,-1,1,0};
			int[] dx = {1,0,0,-1};
			for(int i = 0;i<4;i++) {//
				int nowx = now.x + dx[i];
				int nowy = now.y + dy[i];
				//2.判断没有越界
				if(nowx < 0 || nowx>= boo.length || nowy<0 || nowy>=boo[0].length) {
					continue;
				}
				//3.判断是否可行
				if(mg[nowx][nowy] != 0) {
					continue;
				}
				//4.当前点可行,将当前点标记
				mg[nowx][nowy] = 2;
				//5.记录当前位置是从哪里来的D<L<R<U 下左右上
				char[] s = {'D','L','R','U'};
				boo[nowx][nowy] = s[i];
//				System.out.println(boo[nowx][nowy]);
				//6.将当前点添加到对列
				queue.add(new wz(nowx,nowy));
			}
		}
		return boo;
	}
	
	static class wz{
		int x;
		int y;
		wz(int x,int y){
			this.x = x;
			this.y = y;
		}
	}
}

第五题:RSA 解密(填空题)(数论)
题目描述
RSA 是一种经典的加密算法。它的基本加密过程如下。
首先生成两个质数 p, q,令 n = p · q,设 d 与 (p − 1) · (q − 1) 互质,则可找到 e 使得 d · e 除 (p − 1) · (q − 1) 的余数为 1。
n, d, e 组成了私钥,n, d 组成了公钥。
当使用公钥加密一个整数 X 时(小于 n),计算 C = Xd mod n,则 C 是加密后的密文。
当收到密文 C 时,可使用私钥解开,计算公式为 X = Ce mod n。例如,当 p = 5, q = 11, d = 3 时,n = 55, e = 27。
若加密数字 24,得 243 mod 55 = 19。解密数字 19,得 1927 mod 55 = 24。
现在你知道公钥中 n = 1001733993063167141, d = 212353,同时你截获了别人发送的密文 C = 20190324,请问,原文是多少?

第六题:完全二叉树的权值(程序设计)(树)
题目描述
给定一棵包含 N 个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是 A1, A2, · · · AN,如下图所示:
现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是 1。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · AN 。
【输出格式】
输出一个整数代表答案。
【样例输入】
7
1 4 6 5 3 2 1
【样例输出】
2
【评测用例规模与约定】
对于所有评测用例,1 ≤ N ≤ 100000,−100000 ≤ Ai ≤ 100000。

分析:1.第一行有1个数,第二行有2个数,第三行有4个数。。。

2.可以发现,每行的个数是2^i(2的i次方);

3.优化,利用前缀和将求每行的和时间复杂度降低到O(1)

4.注意:完全二叉树最后一排可以不满,满二叉树最后一排才满

package A组2019年;

import java.util.Scanner;
//AC
//思路:遍历每一层,用前缀和遍历,减少计算次数
public class _6完全二叉树的权值 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		int n = input.nextInt();
		int[] N = new int[n+1];
		
		for(int i=1;i<N.length;i++) {//直接算前缀和
			N[i] = N[i-1] + input.nextInt();
		}
		
		//2.循环遍历每一层
		long sum = Integer.MIN_VALUE;//记录最大的和
		long ans = 0;//第几层最大
		int k =0;//第几层
		int L = 0;//初始位置
		while(true) {
			
			int R = (int)Math.pow(2, k) + L;
			//第一次左右边界都是1
//			if(k == 0)R = (int)Math.pow(2, k);
			
			if(R >= n) {
				R = n;
			}
			
			int nowsum = N[R] - N[L];//当前层的和
			k++;//层数加一
			if(nowsum > sum) {
				sum = nowsum;
				ans = k;
			}
			
			L = R;//将左端点移动到右端点右边一个位置
			
			if(L >= n)break;//左边界大于最大位置就跳出循环
		}
		
		System.out.print(ans);
		
	}
	
}

第七题: 外卖店优先级(程序设计)(模拟)
题目描述
“饱了么”外卖系统中维护着 N 家外卖店,编号 1 ∼ N。每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中。
【输入格式】
第一行包含 3 个整数 N、M 和 T 。
以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到一个订单。
【输出格式】
输出一个整数代表答案。
【样例输入】
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
【样例输出】
1
【样例解释】
6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6, 加入优先缓存。所以是有 1 家店 (2 号) 在优先缓存中。
【评测用例规模与约定】
对于 80% 的评测用例,1 ≤ N, M, T ≤ 10000。
对于所有评测用例,1 ≤ N, M, T ≤ 100000,1 ≤ ts ≤ T ,1 ≤ id ≤ N。

分析:1.首先想暴力做法,双重循环,第一重循环时间,第二重循环商家,每个时刻循环所有的商家,商家有一个订单优先级就加2,没有订单优先级就减1,每次增加优先级后都判断是否进入优先队列,每次降低优先级后判断是否出优先队列,时间复杂度O(n^2)超时;

2.优化:(压缩时间)我们可以发现,循环每次时间都有很多没有订单的商家,需要重复计算很多次。每次遍历时间的时候,只遍历有订单的商家,我们每次获得订单将他当前获得订单的时间保存,下一次用到有它的订单的时候再一起算账;

注意:所有时刻的订单循环完毕后,最后要在T时刻要进行一次所有订单的遍历,将它们沉余的优先级减去

package A组2019年;

import java.util.ArrayList;
import java.util.Scanner;

//1.压缩时间
//AC
//1.暴力
public class _7外卖店优先级 {
  public static void main(String[] args) {
      Scanner input = new Scanner(System.in);
      int N = input.nextInt();//有1~N家外卖店
      int M = input.nextInt();//有M条外卖信息
      int T = input.nextInt();//计算T时刻有多少家外卖在优先级中
      ArrayList<Integer>[] list = new ArrayList[100010];
      for(int i=0;i<list.length;i++) {//初始化list数组
          list[i] = new ArrayList<>();
      }
      for(int i=0;i<M;i++) {
          int ts = input.nextInt();
          int id = input.nextInt();
          list[ts].add(id);
      }

      //2.用户输入完成
//		按时间顺序遍历店铺
      int[][] ans = new int[N+1][2];//每间店铺的优先级和他上一次有订单在什么时候
      boolean[] fist = new boolean[N+1];//店铺是否在优先级中
      for(int i=1;i<=T;i++) {//遍历时间
          for(int j=0;j<list[i].size();j++) {//2.遍历每个时间内所有有订单的商家
              //1.将这次时刻剪掉上一次有订单的时刻
              if(i > ans[list[i].get(j)][1]) {//说明此商家上一个时刻没有订单
                  ans[list[i].get(j)][0] -= i-ans[list[i].get(j)][1]-1;//优先级降低
                  if(ans[list[i].get(j)][0] < 0)ans[list[i].get(j)][0] = 0;//优先级最低为0
                  //判断商家是否掉出优先级队列
                  if(ans[list[i].get(j)][0] <= 3)fist[list[i].get(j)]=false;

              }
              //2.将此商家优先级加2,将上一次有订单时刻标记为现在
              ans[list[i].get(j)][0] += 2;
              ans[list[i].get(j)][1] = i;
              //3.判断此商家能不能加入优先级
              if(ans[list[i].get(j)][0] > 5)fist[list[i].get(j)] = true;


          }


      }

      //3.所有订单已经派送完成,遍历所有店铺在时间T时刻的优先级
      for(int i=1;i<ans.length;i++) {
          //上一次订单时刻不是在T时刻也不是在T-1时刻,那么它的优先级就应该被降低
          if(ans[i][1] < T) {// T=7  2  3  6
//              if(ans[i][1] == T-1) continue;
              ans[i][0] -= T - ans[i][1] ;
              //判断还在不在优先级中
              if(ans[i][0] <= 3)fist[i]=false;
          }
      }


      //测试输出商家优先级
      for(int i=0;i<ans.length;i++) {
          System.out.println(i+"商家"+ans[i][0]);
      }
      //4.输出答案
      int anss = 0;
      for(int i = 0;i<fist.length;i++) {
          if(fist[i]) {
//				System.out.println(i+" ");
              anss++;
          }
      }
      System.out.println(anss);
  }
}


 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源治·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值