第八届蓝桥杯省赛JavaA组部分题解

1.【结果填空】 迷宫

X星球的一处迷宫游乐场建在某个小山坡上。
它是由10x10相互连通的小房间组成的。
房间的地板上写着一个很大的字母。
我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。
X星球的居民有点懒,不愿意费力思考。
他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把100名玩家放入一个个小房间内。
玩家一定要按照地上的字母移动。
迷宫地图如下:
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
请你计算一下,最后,有多少玩家会走出迷宫?
而不是在里边兜圈子。
请提交该整数,表示走出迷宫的玩家数目,不要填写任何多余的内容。
如果你还没明白游戏规则,可以参看一个简化的4x4迷宫的解说图:
p1

分析:简单dfs模拟,设置好出口,遍历所有点(每次遍历需将vis数组重新设为false)就能求出最终结果。
结果:31
代码

import java.util.Scanner;

public class Tp201701 {
	static char[][] a = new char[13][13];
	static boolean[][] vis;
	static int[] dirx = {-1, 1, 0, 0}; //四个方向对应上下左右
	static int[] diry = {0, 0, -1, 1};
	static boolean flag = false;
	static int cnt = 0;
	static int m,n;
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		m=n=10;	
		for(int i = 0; i<13; i++){
			a[0][i] = '0';  //设置出口标志
			a[m+1][i] = '0';
		}
		for(int i = 1; i<=m; i++){
			String tem2 = in.nextLine();//复制样例输入
			String tem = '0'+tem2 +'0';
			a[i] = tem.toCharArray();
		}
		for(int i = 1; i<=m; i++){
			for(int j = 1; j<=n; j++){
				vis = new boolean[m+3][n+3];
				flag = false;
				dfs(i, j);
			}
		}
		System.out.println(cnt);
	}

	private static void dfs(int x, int y) {
		if(flag) return ;
		if(a[x][y] == '0'){
			cnt++;
			flag = true;
			return;
		}
		vis[x][y] = true;
		char ch = a[x][y];
		int dir = -1;
		if(ch == 'D')   dir = 1;
		else if(ch == 'U') dir = 0;
		else if(ch == 'L') dir = 2;
		else if(ch == 'R') dir = 3;
		int dx = x+dirx[dir];
		int dy = y+diry[dir];
		if(!vis[dx][dy]) dfs(dx, dy);
	}
}

2.【结果填空】9数算式

观察如下的算式:
9213 x 85674 = 789314562
左边的乘数和被乘数正好用到了1~9的所有数字,每个1次。
而乘积恰好也是用到了1~9的所有数字,并且每个1次。
请你借助计算机的强大计算能力,找出满足如上要求的9数算式一共有多少个?
注意:

  1. 总数目包含题目给出的那个示例。
  2. 乘数和被乘数交换后作为同一方案来看待。

分析:爆破,不过怎么暴力求解是决定时间复杂度的关键,一开始我用左边的数i从1到100000,左边第二个操作数j则是(j,1000000),时间复杂度太大。。后来想起来在紫书上看过的暴力的设计方法,我们可以从结果开始枚举,因为结果的范围是确定的,[1e8,1e9),每次枚举结果就快了不少。Ps:蓝桥很喜欢类似9数这种题a
结果:1625
代码

public class Tp201702 {
	static int[] num;
	static int cnt = 0;
	static void toNum(int x){
		while(x != 0){
			num[x%10]++;
			x /= 10;
		}
	}
	private static boolean check() {
		for(int i = 1; i<=9; i++){
			if(num[i] != 1) return false;
		}
		if(num[0] != 0) return false;
		return true;
	}
	public static void main(String[] args) {
		int st = (int)1e8;
		int ed = (int)1e9;
		for(int res = st; res<ed; res++){
			num = new int[11];
			toNum(res);
			if(!check()) continue;
			for(int i = 1; i<(int)(1e5); i++){
				if(res % i != 0) continue;
				int j = res/i;
				if(j <= i) break;
				num = new int[11];
				toNum(i);
				toNum(j);
				if(check()) {
					System.out.println(i+" "+j+" = "+res);
					cnt++;
				}
			}
		}
		System.out.println(cnt);
	}
}

5、【代码填空】 字母组串

由 A,B,C 这3个字母就可以组成许多串。
比如:“A”,“AB”,“ABC”,“ABA”,“AACBB” …
现在,小明正在思考一个问题:
如果每个字母的个数有限定,能组成多少个已知长度的串呢?
他请好朋友来帮忙,很快得到了代码,
解决方案超级简单,然而最重要的部分却语焉不详。
请仔细分析源码,填写划线部分缺少的内容。
对于下面的测试数据,小明口算的结果应该是:
6
19
注意:只填写划线部分缺少的代码,不要提交任何多余内容或说明性文字。

public class A
{
	// a个A,b个B,c个C 字母,能组成多少个不同的长度为n的串。
	static int f(int a, int b, int c, int n)
	{
		if(a<0 || b<0 || c<0) return 0;
		if(n==0) return 1; 
		
		return ________________________________;  //填空
	}
	
	public static void main(String[] args)
	{
		System.out.println(f(1,1,1,2));
		System.out.println(f(1,2,3,3));
	}
}

分析:简单递归,返回分别取三种情况的结果和
代码

f(a-1, b, c, n-1)+f(a, b-1, c, n-1)+f(a, b, c-1, n-1)

6、【代码填空】 最大公共子串

最大公共子串长度问题就是:
求两个串的所有子串中能够匹配上的最大长度是多少。
比如:“abcdkkk” 和 “baabcdadabc”,
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。
下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。
请分析该解法的思路,并补全划线部分缺失的代码。

public class Main
{
	static int f(String s1, String s2)
	{
		char[] c1 = s1.toCharArray();
		char[] c2 = s2.toCharArray();
		
		int[][] a = new int[c1.length+1][c2.length+1];
		
		int max = 0;
		for(int i=1; i<a.length; i++){
			for(int j=1; j<a[i].length; j++){
				if(c1[i-1]==c2[j-1]) {
					a[i][j] = __________________;  //填空 
					if(a[i][j] > max) max = a[i][j];
				}
			}
		}
		
		return max;
	}
	
	public static void main(String[] args){
		int n = f("abcdkkk", "baabcdadabc");
		System.out.println(n);
	}
}

注意:只提交缺少的代码,不要提交已有的代码和符号。也不要提交说明性文字。
分析:简单dp入门题,直接上代码
代码

a[i-1][j-1]+1;

7、【编程大题】 正则问题

考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6。
输入
一个由x()|组成的正则表达式。输入长度不超过100,保证合法。
输出
这个正则表达式能接受的最长字符串的长度。
例如,
输入:
((xx|xxx)x|(x|xx))xx
程序应该输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。

分析:一开始想简单了想用栈模拟再用贪心策略,没考虑到以下情况:
(xxx|xx|x)的情况,也就是**|的数量在一对括号中不止出现一次,以及其他情况,后来被提醒用dfs,没遇到一次(**就进入一次递归,于是代码就变得比较简单了。
代码

import java.util.Scanner;

public class Tp201707 {
	static int i,len;
	static char[] arr;
	static int dfs(){
		int sum = 0, left = 0;
		while(i < len){
			if(arr[i] == '('){
				i++;
				sum += dfs();
				continue;
			}
			else if(arr[i] == ')'){
				i++;
				break;
			}
			else if(arr[i] == 'x')
				sum++;
			else {
				left = Math.max(sum, left);
				sum = 0;
			}
			i++;	
		}
		
		return Math.max(left, sum);
	}
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		arr = in.nextLine().toCharArray();
		len = arr.length;
		System.out.println(dfs());
	}

}

8、【编程大题】 包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
例如,
输入:
2
4
5
程序应该输出:
6
再例如,
输入:
2
4
6
程序应该输出:
INF
样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
提交程序时,注意选择所期望的语言类型和编译器类型。

分析:欧几里得+dfs,由判断所有笼数的最大公约数是不是1得到结果是否为INF,证明略。。后暴搜一波。(在官网的练习系统上测了一下,最后一个测试点超时。。To be continued…
代码

import java.util.Arrays;
import java.util.Scanner;

public class Tp201708 {
	static int cnt,ed,n;
	static boolean flag = false;
	static int[] arr;
	static void dfs(int cur){
		if(flag) return ;
		if(cur == ed){
			flag = true;
			return;
		}
		if(cur > ed) return;
		for(int i = 0; i<n; i++){
			dfs(cur+arr[i]);
		}
	}
	static int gcd(int a, int b){
		if(b == 0) return a;
		return gcd(b, a%b);
	}
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		arr = new int[n];
		for(int i = 0; i<n; i++){
			arr[i] = in.nextInt();
		}
		int gcdnum = arr[0]; 
		for(int i = 1; i<n; i++){
			gcdnum = gcd(gcdnum, arr[i]);
		}
		//如果笼数的最大公约数不是1就是INF
		if(gcdnum != 1){
			System.out.println("INF");
			return;
		}
		Arrays.sort(arr);
		int num = 0; //记录最大连续能取到的数
		for(int i = 1; i<=100*100; i++){
			ed = i;
			flag = false;
			dfs(0);
			if(flag) num++;
			else {
				num = 0;
				cnt++;
			}
			if(num >= arr[0]){ //最大连续能取到的数大于最小笼数 以后的就都能取到了
				System.out.println(cnt);
				break;
			}
		}
		
	}

}

9、【编程大题】 分巧克力

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。

分析:本来以为是几何题,后来仔细看题发现只是简单运算,所以,不要方,问题不大。一开始想着爆破,但是一定是会超时的,后来从大神那了解到用二分,自己当初为啥没想到TAT。。(已在官网练习系统上AC,重写输入输出流才过TAT
这里写图片描述
代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Main {
	static int maxn = (int)1e9+10;
	static int n,k;
	static int[] x,y;
	static int check(int tem){
		int cnt = 0; //记录切边长为tem的蛋糕一共能切出多少块
		for(int i = 0; i<n; i++){
			cnt += (x[i]/tem)*(y[i]/tem);
		}
		return cnt;
	}
	public static void main(String[] args) throws IOException {
		StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		in.nextToken();
		n = (int)in.nval;
		in.nextToken();
		k = (int)in.nval;
		x = new int[n];
		y = new int[n];
		for(int i = 0; i<n; i++){
			in.nextToken();
			x[i] = (int)in.nval;
			in.nextToken();
			y[i] = (int)in.nval;
		}
		int left = -1, right = maxn;
		int res = 0; //res记录满足条件的最大边长
		//二分
		while(right-left > 1){
			int mid = (left+right)/2;
			if(check(mid) >= k) {
				res = Math.max(res, mid);
				left = mid;
			}
			else right = mid;
		}
		out.println(res);
		out.flush();
	}
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值