Post

Com8_第八届蓝桥杯大赛软件赛省赛Java大学A组

提交的时候一定要记得,把代码中用于测试的输出去掉😅

1. 迷宫:最短路径问题

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package lanQ8;

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

public class L1 {
	static int n = 30, m = 50;
	static String[] maze = { "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" };

	static class State {
		int x, y;
		String path;

		State(int x, int y, String path) {
			this.x = x;
			this.y = y;
			this.path = path;
		}
	}

	// 方向数组:按D<L<R<U顺序
	static int[] dx = { 1, 0, 0, -1 };
	static int[] dy = { 0, -1, 1, 0 };
	static char[] dirChar = { 'D', 'L', 'R', 'U' };

	public static void main(String[] args) {
		int[][] dist = new int[n][m]; // 到某个点的最短步数
		String[][] pathMap = new String[n][m]; // 到某个点的最短路径,记录方向

		for (int i = 0; i < n; i++) {
			Arrays.fill(dist[i], Integer.MAX_VALUE); // 用一个值填充整个数组
			Arrays.fill(pathMap[i], null);
		}

		Queue<State> queue = new LinkedList<>();
		queue.add(new State(0, 0, "")); // 队列先进先出,实现BFS
		dist[0][0] = 0;
		pathMap[0][0] = "";

		while (!queue.isEmpty()) {
			State cur = queue.poll();
			int x = cur.x, y = cur.y;
			String path = cur.path;

			for (int d = 0; d < 4; d++) { // 尝试每种方向
				int nx = x + dx[d];
				int ny = y + dy[d];
				if (nx < 0 || nx >= n || ny < 0 || ny >= m)
					continue;
				if (maze[nx].charAt(ny) == '1')
					continue;

				int newDist = dist[x][y] + 1;
				String newPath = path + dirChar[d]; // 拼接路径

				if (newDist < dist[nx][ny]) {
					dist[nx][ny] = newDist;
					pathMap[nx][ny] = newPath;
					queue.add(new State(nx, ny, newPath));
				} else if (newDist == dist[nx][ny] && newPath.compareTo(pathMap[nx][ny]) < 0) {
					pathMap[nx][ny] = newPath;
					queue.add(new State(nx, ny, newPath));
				}
			}
		}
		System.out.println(pathMap[n - 1][m - 1]);
	}
}

思路:

  • 题目要求:找出最短路径,且字典序最前;

  • BFS:层级遍历,可以保证第一次到达终点一定是最短路径;

    DFS: 适合用于找所有解

  • 使用class存储每一个点的状态:坐标值,及从起点到达的方式;

  • 使用三个数组表示方向,从来没想过的思路;

  • 两个辅组数组
    • dist: 到某个点的最短步数;
    • pathMap: 到某个点的最短路径;
  • 使用队列表示还能继续处理的点,先进先出;
  • 更新逻辑:
    • 如果出界或是墙,跳过;
    • 如果newDist < dist[nx][ny] : 找到更短路径,更新;
    • 如果路径长度相同,newPath < pathMap[nx][ny], 字典序更小,更新;

注意:

队列和栈可以统一使用Deque<Integer> q = new ArrayDeque<>()

总结:

刚好是没复习到的图论,很会考😁


2. 9数算式

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package lanQ8;

import java.util.ArrayList;
import java.util.List;

public class L2 {

	static int cnt = 0;

	public static void main(String[] args) {
		for (int i = 1; i <= Math.sqrt((double) 98765432); i++) {
			List<Integer> left = new ArrayList<>();
			int temp = i;
			boolean flag = false;
			while (temp > 0) {
				int mid = temp % 10;
				temp /= 10;
				if (left.contains(mid) || mid == 0) {
					flag = true;
					break;
				}
				left.add(mid);
			}
			if (flag)
				continue;

			for (int j = 98765432; j > (int) Math.sqrt((double) 98765432); j--) {
				List<Integer> mid = new ArrayList<>();
				j = Math.min(j, 987654321 / i);
				int temp1 = j;
				boolean flag1 = false;
				if (i * j < 123456789)
					break;
				if (i * j > 987654321 || i * j < 123456789)
					continue;
				while (temp1 > 0) {
					int mid1 = temp1 % 10;
					temp1 /= 10;
					if (left.contains(mid1) || mid.contains(mid1) || mid1 == 0) {
						flag1 = true;
						break;
					}
					mid.add(mid1);
				}

				if (flag1)
					continue;
				else {
					List<Integer> right = new ArrayList<>();  // 动态数组
					long resNum = i * j;
					if (resNum < 123456789)
						continue;
					long temp2 = resNum;
					boolean flag2 = false;
					while (temp2 > 0) {
						int mid2 = (int) (temp2 % 10);
						temp2 /= 10;
						if (right.contains(mid2) || mid2 == 0) {
							flag2 = true;
							break;
						}
						right.add(mid2);
					}
					if (flag2)
						continue;
					else {
						cnt++;
						System.out.println(i + "*" + j + "=" + resNum);
					}
				}
			}
		}
		System.out.println(cnt);
	}
}

唯一AC,建三个动态数组,用来判断是否有重复的数字;

把无法进行下去的条件筛的干净一点,即可快速获得结果


3. 魔方状态

alt text

229878

思路:(根本看不懂,也并没有找到很完整的解题思路)

alt text

alt text


4. 方格分割

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package lanQ8;

public class L4 {
	static int[][] v = new int[7][7];
	static int[] dx = { 0, 1, 0, -1 };
	static int[] dy = { 1, 0, -1, 0 };
	static int cut = 0;

	static void dfs(int x, int y) {
		if (x == 0 || x == 6 || y == 0 || y == 6) {
			cut++;
			return;
		}

		for (int i = 0; i < 4; i++) {
			int tx = x + dx[i];
			int ty = y + dy[i];

			if (v[tx][ty] == 0) {
				v[tx][ty] = 1;
				v[6 - tx][6 - ty] = 1;
				dfs(tx, ty);
				v[tx][ty] = 0;
				v[6 - tx][6 - ty] = 0;
			}
		}
	}

	public static void main(String[] args) {
		v[3][3] = 1;
		dfs(3, 3);
		System.out.print(cut / 4);  // 旋转有4种
	}
}

思路:

  • 不考虑格子,而是考虑边框,那么形状其实是7*7;
  • 由于本题要求找出所有路径,就像第1题所说,使用dfs;
  • 中心点是一定要经过的,v[3][3]设置为1:
    • 向边界探索,这里也同第一题,试探上下左右四个方向;
    • 走到边界,种类加1;
  • 试探体现在:bfs递归调用完,要把当前节点的状态设置为0;

总结:

又考了dfs, 哪里不会考哪里😅


5. 正则问题

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package lanQ8;

import java.util.Scanner;

public class L5_2 {

	static int pos = -1; // 下标
	static String s = null;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		s = sc.nextLine();
		System.out.println(dfs());
		sc.close();
	}

	private static int dfs() {
		int current = 0; // 当前积攒的x数目
		int max = 0; // 最终x的最大个数
		while (pos < s.length() - 1) {
			pos++;
			if (s.charAt(pos) == '(') {
				current += dfs(); // 去到递归下一层
			} else if (s.charAt(pos) == 'x') {
				current += 1; // 遇到x,积攒的x数目就加一
			} else if (s.charAt(pos) == '|') {
				max = Math.max(current, max);
				current = 0; // 当前x处理完,归零
			} else { // 遇到),回到上一层
				break;
			}
		}
		return Math.max(current, max);
	}
}

本来用的算术表达式的计算方法,即使用符号栈、数字栈,只对了一半没法AC

思路:

  • 设置两个状态量:
    • current: 积攒的还未参与过运算的x数目;
    • max: 每次都保留最大值,可作为结果;
  • 每一组括号视为递归的一层

总结:

妙啊!!


6. 包子凑数

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 个人AC代码
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] a = new int[n];
		int min = 10000;
		boolean flag = false;
		for (int i = 0; i < n; i++) {
			a[i] = sc.nextInt();
			if (a[i] % 2 != 0)
				flag = true;
			if (a[i] < min)
				min = a[i];
		}

		if (!flag) {
			System.out.println("INF");
			return;
		}

		int[] dp = new int[200000];
		for (int i = 0; i < min; i++) {
			dp[i] = 0;
		}
		for (int i = 0; i < n; i++) {
			dp[a[i]] = 1;
		}

		for (int i = min; i < 200000; i++) {
			for (int j = 0; j < n; j++) {
				if (i >= a[j]) {
					dp[i] += dp[i - a[j]];
				}
			}
		}

		int cnt = 0;
		for (int i = 1; i < 200000; i++) {
			if (dp[i] == 0) {
				cnt++;
			}
		}
    if(cnt > 5000) System.out.println("INF");
		else System.out.println(cnt);
	}
}

思路:

  • 很明显的dp题,这里dp[i]表示买i个包子的凑数法,但并没想到状态转移方程;

  • 想到本题只是判断是否能够凑出,并未要求给出正确方案数,因此想出以下错误方程:

    dp[i] += dp[i - a[j]]

  • 这样只判断不为0,即表示能够凑出;

  • 本代码的问题是,不知道要遍历到多少个包子,才能把所有凑不出来的都遍历到。(可以设为1e7)


7. 分巧克力

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 个人AC代码
package lanQ8;

import java.util.Scanner;

public class L7 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int k = sc.nextInt();
		int[][] a = new int[n][2];
		int max = 0;
		for (int i = 0; i < n; i++) {
			a[i][0] = sc.nextInt();
			a[i][1] = sc.nextInt();
			if (a[i][0] > max)
				max = a[i][0];
			if (a[i][1] > max)
				max = a[i][1];
		}
		for (int i = max; i >= 1; i--) {
			int sum = 0;
			for (int j = 0; j < n; j++) {
				int hCnt = a[j][0] / i;
				int wCnt = a[j][1] / i;
				sum = sum + hCnt * wCnt;
				if (sum >= k) {
					System.out.println(i);
					break;
				}
			}
			if (sum >= k) {
				break;
			}
		}
	}
}

个人感觉最简单的一道题,看来不一定越后面的题越难

放一个二分查找的代码,前面的太暴力了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package lanQ8;

import java.util.Scanner;

public class L7 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int k = sc.nextInt();
		int[][] a = new int[n][2];
		int max = 0;
		for (int i = 0; i < n; i++) {
			a[i][0] = sc.nextInt();
			a[i][1] = sc.nextInt();
			if (a[i][0] > max)
				max = a[i][0];
			if (a[i][1] > max)
				max = a[i][1];
		}

		int left = 1, right = max;
		int ans = 0;

		while (left <= right) {
			int mid = (left + right) / 2;
			if (canCut(a, n, k, mid)) {
				ans = mid;
				left = mid + 1;
			} else {
				right = mid - 1;
			}
		}

		System.out.print(ans);
	}

	private static boolean canCut(int[][] a, int n, int k, int len) {
		int count = 0;
		for (int i = 0; i < n; i++) {
			count += (a[i][0] / len) * (a[i][1] / len);
			if (count >= k)
				return true;
		}
		return false;
	}
}

8. 油漆面积

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package lanQ8;

import java.util.Scanner;

public class L8 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		boolean[][]a = new boolean[10000][10000];
		int n = sc.nextInt();
		int sum = 0;
		
		for(int i = 0;i<n;i++) {
			int x1 = sc.nextInt();
			int y1 = sc.nextInt();
			int x2 = sc.nextInt();
			int y2 = sc.nextInt();
			
			if(x1 > x2) {
				int t = x1;
				x1 = x2;
				x2 = t;
			}
			if(y1 > y2) {
				int t = y1;
				y1 = y2;
				y2 = t;
			}
			
			for(int j = x1;j<x2;j++) {
				for(int k = y1;k<y2;k++) {
					if(a[j][k] == false) sum ++;
					a[j][k] =true;
				}
			}
		}
		System.out.print(sum);
	}
}

第二简单的一题,注意是算面积不是算点。


This post is licensed under CC BY 4.0 by the author.