Java实现蓝桥杯第十一届2020年JavaB组真题

【不重要的前言】

去年参赛前还是信心满满的。第一次参赛没什么经验,七段码和排序这两道填空题卡太久了,导致后面大题会做的没有拿下。
怎么说呢,还是自己水平不够。只拿了省二。应该就对了前三道填空题和后面两道大题。
今年重新报名Java B组,希望经过一年的学习能圆当初的遗憾,尽力写吧。

第十一届2020年JavaB组原题
提取码:g8d6

试题 A:门牌制作

在这里插入图片描述

【思路】
数的分解

package 第十一届;
/**
* @author JohnnyLin
* @version Creation Time:2021年4月1日 下午8:28:46
* 答案:624
*/
public class _A门牌制作 {
	
	static int getCnt(int num) {
		int ans = 0;
		while( num > 0) {
			if( num % 10 == 2) ans++;
			num /=10;
		}
		return ans;
	}

	public static void main(String[] args) {
		int res = 0;  //624
		for(int i = 1; i <= 2020; i ++)
				res += getCnt(i);
		System.out.println(res);
		//System.out.println(getCnt(3));

	}

}

试题 B: 寻找 2020

【题目描述】

在这里插入图片描述

【思路】

有三种走法,往下走四个格子,对角(从左上到右下)走四个格子,往上走四个格子。 dfs将每种格子的可能走法枚举,同时统计能走出2020的路径的方案数。
答案:16520
在这里插入图片描述

package 第十一届;

import java.util.Scanner;


/**
* @author JohnnyLin
* @version Creation Time:2021年4月1日 下午8:51:23
*/
public class _B寻找2020 {
	static int N = 310;
	static char f[][] = new char[N][N];
	//右 右下 下 左下
	static int dir[][] = { {0, 1}, {1, 1}, {1, 0}};
	static int ans = 0;
	static int n, m;
	
	public static void dfs(int x, int y, int step, int d, int res) {
		if( step == 4) {
			if( res == 2020) ans ++;
			return;
		}
		int a = x + dir[d][0], b = y + dir[d][1];
		if( a >= 0 && a < m && b >= 0 && b < n)
			dfs(a, b, step + 1, d,  res * 10 + (f[a][b] - '0')  );
		
	}
	
	public static void main(String[] args) {
		Scanner reader = new Scanner(System.in);
		n = reader.nextInt();
		m = reader.nextInt();
		
		for(int i = 0; i < n; i ++) {
			String string = reader.next();
			for(int j = 0; j < m; j ++)
				f[i][j]= string.charAt(j);
		}
		for(int d = 0; d < 3; d ++) {
			for(int i = 0; i < n; i ++) {
				for(int j = 0; j < m; j ++)
					dfs(i, j, 1, d, f[i][j] - '0');
			}
		}
		System.out.println(ans);
				
		
	}

}


试题 C: 蛇形填数

【题目描述】
在这里插入图片描述

【思路】

在这里插入图片描述

发现规律,横纵坐标之和为定值,且该定值为在该条对角线的数字个数-1。对角线数字的个数为1、2、 3、 4、 5、 6……
编号(从0开始)为奇数的对角线,自右上向左下。编号为偶数的对角线,自左下向右上。

package 第十一届;
/**
* @author JohnnyLin
* @version Creation Time:2021年4月1日 下午9:28:49
*/
public class _C蛇形填数 {
	static int N = 50;
	static int f[][] = new int[N][N];
	
	
	public static void main(String[] args) {
		
		int num = 1;
		int k = 0; //横纵坐标和为k
		while( num <= 1000) {
			if( (k & 1) == 1) {  //编号为奇数的对角线: x坐标从0开始
				for(int x = 0; x <= k; x ++)
					f[x][k - x] = num ++;
			}else {//编号为偶数的对角线:x坐标从k开始
				for(int x = k; x >= 0; x --)
					f[x][k - x] = num ++;
			}
			
			//下一条对角线
			k ++;		
		}
		System.out.print(f[19][19] + "\t");
		/*
		for(int i = 0; i < N; i ++) {
			for(int j = 0; j < N; j ++)
				System.out.print(f[i][j] + "\t");
			System.out.println();
		}*/
			
		
		

	}

}

实际上就这题而言也不用每个都求出。写出蛇形矩阵的一部分,会发现规律:

(行号,列号)
(1,1)1
(2,2)5
(3,3)13
(4,4)25
(5,5)41
(6,6)61
(i,i)每隔一项,差4 * (i - 1) ( i = 1 、2、3……i - 1)
(n,n)1 + (1 + (n - 1))*(n - 1) *4 /2

代入求得761

试题 D:七段码

【题目描述】

【思路】

使用邻接链表存储每条边的关系,如
q[a]:b、f
q[b]:a、c 、g、
q[c]:b、d、g
q[d]:c、e
q[e]:d、f、g
q[f]:a、e、g
q[g]:b、c、f、e

然后从1到7枚举组成数字的段数,分别以每一个字母开头取枚举,

原先以为重复计算的只有反转后相同的排列,所以用了下面的方法:

		//去掉翻转之后相同的数字串
		Set<String> ans = new CopyOnWriteArraySet<>();
		for(String s: set) {
			if( s.length() == 1) {//一个字符 反转之后必定有本身
				ans.add(s);
				continue;
			}
			//{ab, ba}			{ab,}
			StringBuffer sb =  new StringBuffer(s);//ab  ba
			String ns = sb.reverse().toString();//ba  ab
			if( ans.contains(ns)) continue;//其逆序已经存在则跳过不统计该数字
			ans.add(s);
			
		}
		for(String s: ans) {
			System.out.println(s);
		}
		//a 的ASCII码值为97
		System.out.println(ans.size());

后来才发现不止:dcge、edcg也是重复的一种。

想到要是每一种方案都能按照升序拍好,那就就好统计了。

package 第十一届;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;



/**
* @author JohnnyLin
* @version Creation Time:2021年4月1日 下午10:20:51
* 类说明
*/
/*
录入数据:
a b
a f
b a
b c
b g
c b
c d
c g
d c
d e
e d
e f
e g
f a
f e
f g
g b
g c
g f
g e
0

答案:
abceg
de
def
beg
deg
abcef
bceg
cefg
abfg
abcdfg
bcdefg
ab
ef
abefg
eg
bfg
af
cde
cdg
abcfg
adef
bcdg
bcde
defg
abeg
abef
bcdeg
abdefg
abdef
bc
fg
bcd
aef
ceg
bg
bcg
cdefg
cdfg
bcdfg
abcefg
abcdf
a
cd
b
c
abc
afg
d
cg
abf
e
cfg
f
g
abg
efg
abcde
abcg
abcf
befg
bcfg
cdef
cdeg
abcd
abcdef
abcdeg
abcdefg

答案:67
 
 
 */
public class _D七段码 {
	//初始化为N个空的ArrayList, 表示每一个q[i]都为空
	static int N = 7;
	static List<Character> [] q =  new ArrayList[N];
	static boolean st[] = new boolean[N]; //每一段只允许被使用一次
	static Set<String> set = new CopyOnWriteArraySet<>();
	public static void dfs(int father,int son, int cnt, String s) {
		//System.out.println(son + " "+cnt + " "+s);
		if( cnt == 1) {
			set.add(s);
			return;
		}
		st[son] = true;
		for(Character c: q[son]) {//枚举son的邻接边
			int next = c - 'a';
			if( !st[next] && next != father) {
				String ns = Character.toString(c);
				dfs(son, c - 'a', cnt - 1, s + ns);
			}
			
			
		}
		
		
	}
	public static void main(String[] args) {
		
		for(int i = 0; i < N; i ++)
			q[i] = new ArrayList<Character>();
		
		Scanner reader = new Scanner(System.in);
		while(true) {
			String s[] = reader.nextLine().split(" ");
			if( s[0].equals("0") ) break;
			char x = s[0].charAt(0), y = s[1].charAt(0);
			q[ x - 'a'].add(y);
		}
		
		for(int i = 1; i <= 7; i ++) {//枚举段数
			for(int start = 0; start < 7; start ++) {//以哪个字母开头
				Arrays.fill(st,false);
				char c = (char) ('a' + start);
				String s = Character.toString(c);
				dfs(- 1, start, i, s);
			}
			
		} 
		
		Set<String> ans = new HashSet<>(); 
		for(String s: set) {
			char c[] = s.toCharArray();
			Arrays.sort(c);
			//c.toString() 得到的是地址
			String nString = new String(c);
			if( !ans.contains(nString)) ans.add(nString);
		}
		for(String s: ans)
				System.out.println(s);
		System.out.print(ans.size());
			
		
		

	}

}

看了一些其他大佬的答案 好像自己算错了 ,别人答案是:80
代码源自 江先生的故事 2020第十一届蓝桥杯JavaB组省赛总结

package 第十一届;

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

/**
* @author JohnnyLin
* @version Creation Time:2021年4月4日 下午5:40:55
* 类说明
*/

 
  
public class _D七段码2 {
    static int N = 100000 * 4 + 5;
    static int M = 1000 + 5;
    static int mod = 1000000009;
    static Scanner cin = new Scanner(System.in);
     
    static int ans = 0, n, m;
    static ArrayList<Integer> g[] = new ArrayList[N]; //建图 第i个数组的元素都跟i相连
    static boolean vis[] = new boolean[N]; //判断当前这条线段是否被递归过
    static boolean dp[] = new boolean[N]; //二进制判重
    static int a[] = new int[1<<8];
    
    public static void main(String[] args) {
    	for(int i = 1; i <= 7; i++)
    		g[i] = new ArrayList<Integer>();
    	//这里就是赋予数字意义 重新连边
    	add(1, 2); add(1, 6); add(2, 7); add(2, 3); add(3, 4); 
    	add(4, 5); add(5, 6); add(5, 7); add(6, 7); add(3, 7);
    	for(int i = 1; i <= 7; i++) {
    		vis[i] = true; //这里枚举是保证至少有一条灯管是亮着的
    		dfs(i , 1);
    		vis[i] = false;
    	}
    	System.out.println(ans);
	}
    
    //建立联通块的同时
	private static void dfs(int u, int k) {
		
		a[k] = u;  //递归的第k条线段是数字编号为u的线段
		check(k); //对每个状态都判重
		//这里从1枚举到k就是枚举当前联通块相连的边
		for(int i = 1; i <= k; i++) {
			for(int v: g[a[i]]) { 
				if(vis[v]) continue;
				vis[v] = true;
				dfs(v, k + 1);
				vis[v] = false;
			}
		}
	}

	private static void check(int k) {
		int res = 0;
		for(int i = 1; i <= k; i++) {//因为每个线段的数字不同 代表的二进制数字也就不同
			res += (1<<a[i]);				
		}
		if(dp[res]) return;
		dp[res]  = true;
		ans++;
	}

	private static void add(int i, int j) {
		g[i].add(j); g[j].add(i);
	}

	

	
}


试题 E:排序

【题目描述】
在这里插入图片描述
【思路】

容易发现,相同交换次数,完全逆序时,所需的字符串最少。又要求字典序最小,所以最左端应该是越靠近a越好。理想字符串形如 : ……edcba。 假设完全逆序,最少需要字符串为x个,则交换次数为:(1 + x - 1)*(x - 1) / 2 >= 100,解得x >=15。14个字符完全逆序交换次数为98,15个字符完全逆序交换次数为105。所以字符长度至少是15。这样一来就得到了枚举的字符串长度。onmlkjihgfedcba的交换次数为105,想到只要有一个字符少交换5次,其他字符相对顺序不变,而实际上这样是不可能做到的,因为少交换意味着要在原来逆序的位置上往右移动,这样一来其他字符的相对顺序也就发生了改变。
但换种思路,我只要5个字符,每个字符都少移动1次,其他字符相对正确位置不发生改变即可。这样也就是原逆序字符串左边的第6个字符j往左移动5位即可 。即:jonmlkihgfedcba。 然后代入程序检验。

package 第十一届;

/**
* @author JohnnyLin
* @version Creation Time:2021年4月2日 下午12:02:40
*/
public class _E排序 {
	public static void bubble_sort(char [] c) {
		/*
		 * 双指针一左(j)一右(i)
		 * 如果c[j] > c[j + 1],则交换i、j所指元素且i ++、 j ++
		 * 否则
		 * 
		 */
		int n = c.length;
		int cnt = 0; //交换次数
		for(int i = 0; i < n - 1; i ++) {
			for( int j = 0; j < n - 1 - i; j ++) {
				if( c[j] > c[j + 1]) {
					char tmp = c[j + 1];
					c[j + 1] = c[j];
					c[j] = tmp;
					cnt ++;
				}
			}
		}
			
		System.out.println(new String(c));
		System.out.println(cnt);
		
		
	}
	
	public static void main(String[] args) {
		//abcdefghijklmno
		char c[] = "jonmlkihgfedcba".toCharArray();
		//char c[] = "onmlkjihgfedcba".toCharArray();
		bubble_sort(c);

	}

}

试题 F:成绩分析

【题目描述】
在这里插入图片描述

在这里插入图片描述

【思路】
白给题
String.format("%.2f", ans )保留小数,注意sum为int类型,做除法的时候要强转避免精度丢失。

package 第十一届;

import java.util.Scanner;

/**
* @author JohnnyLin
* @version Creation Time:2021年4月3日 下午4:40:51
*/
public class _F成绩分析 {

	public static void main(String[] args) {
		
		Scanner reader = new Scanner(System.in);
		int n = reader.nextInt();
		int f [] = new int[n];
		int min = 100, max = 0;
		for(int i = 0; i < n; i ++)
			f[i] = reader.nextInt();
		int sum = 0;
		for(int i = 0; i < n; i ++) {
			if( f[i] > max) max = f[i];
			if( f[i] < min) min = f[i];
			sum += f[i];
		}
		
		System.out.println(max);
		System.out.println(min);
		double ans = sum / (double) n;
		System.out.printf(String.format("%.2f", ans ));
		/*
		double t = 71.29571428571429 ;
		System.out.printf(String.format("%.2f\n",  t));//71.30
		double s = 71.23471428571429 ;
		System.out.printf(String.format("%.2f\n", s ));//71.23
		*/

	}

}

试题 G:单词分析

【题目描述】
在这里插入图片描述

【思路】
白给题 开个数组统计就可了

package 第十一届;

import java.util.Scanner;

/**
* @author JohnnyLin
* @version Creation Time:2021年4月3日 下午4:58:20
*/
public class _G单词分析 {

	public static void main(String[] args) {
		Scanner reader = new Scanner(System.in);
		String s = reader.next();
		char c [] = s.toCharArray();
		int cnt[] = new int[26];
		int n = c.length;
		for(int i = 0; i < n; i ++) 
			cnt[ c[i] - 'a'] ++;
		int index = 0;
		for(int i = 1; i < 26; i ++)
			if(cnt[i] > cnt[index] ) index = i;
		char ch =(char) (index + 'a');
		System.out.println( ch);
		System.out.println( cnt[index]);

	}//nlanga

}

试题 H: 数字三角形

【题目描述】


在这里插入图片描述

【思路】
就是要求在向左下和向右下的步数之差不超过1的情况下,求走过的路径的最大值。
所以只要dfs搜索一遍,在叶子结点的时候判断从该路径过来的左右步数之差以及更新路径最大值。
有搜索的方向是向下的所以不用担心走回头路的问题,所以不用标记数组。且不会越界。

在这里插入图片描述

package 第十一届;

import java.util.Scanner;


/**
* @author JohnnyLin
* @version Creation Time:2021年4月3日 下午5:14:28
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

*/
public class _H数字三角形 {
	static int N = 110;
	static int f[][] = new int[N][N];
	//下、 右下
	static int dir[][] ={{1, 0}, {1, 1}};
	static int ans = 0, n;
	
	/**
	 * 
	 * @param x
	 * @param y
	 * @param sum 当前路径和
	 * @param left	走左边的次数
	 * @param right 走右边的次数
	 */
	static void dfs(int x, int y, int sum, int left, int right) {
		if(x == n - 1) {
			if( Math.abs( left - right) <= 1) {
				if( sum > ans) ans = sum;
			}
			return;
		}
		for(int i = 0; i < 2; i++) {
			int a = x + dir[i][0], b = y + dir[i][1];
			dfs(a, b, sum + f[a][b], left + (i == 0? 1:0), right + (i == 1? 1:0));
		}
	}
	public static void main(String[] args) {
		Scanner reader =  new Scanner(System.in);
		n = reader.nextInt();
		for(int i = 0; i < n; i ++)
			for(int j = 0; j <= i; j ++)
				f[i][j] = reader.nextInt();
		dfs(0, 0, f[0][0], 0, 0);
		System.out.println(ans);

	}

}

试题 I:子串分值和

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

【思路】
直接做法是求出所有子串,需要O(n^2)时间复杂度,然后统计每个子串的分值需要O(n)的复杂度,总时间复杂度为O( n ^3)。这样只能过掉n<=100的数据。
但思考了一下,其实统计子串分值的过程可以不用,只需使用HashSet统计[i,j]区间的不同字符个数。

package 第十一届;

import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

/**
* @author JohnnyLin
* @version Creation Time:2021年4月3日 下午5:54:04
*/
public class _I子串分值和 {

	public static void main(String[] args) {
		Scanner reader =  new Scanner(System.in);
		String s =  reader.next();
		char c[] = s.toCharArray();
		int n = s.length();
		
		int ans = 0; //统计分值
		for(int i = 0; i < n; i ++) {
			Set<Character> sub = new HashSet<>();
			for(int j = i; j < n; j ++) {
				sub.add(c[j]);
				ans += sub.size();
			}
		}
		System.out.println(ans);
	}

}

算法时间复杂度为O(n^2),只能AC 60%的数据。
要想全部数据AC,算法时间复杂度需要O(n*logn)以内。

  • 8
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值