蓝桥杯第十三届Java大学生B组省赛真题及题解

试题 A: 星期计算

1711702041687

[题解]

方法一:费马小定理

费马小定理:若 m 是一个质数, 整数 a 不是 m 的倍数,则 a m − 1 ≡ 1 ( m o d   m ) ​ a^{m - 1} \equiv 1 (mod \, m)​ am11(modm) 成立

代入可得 2 0 6 ≡ 1 ( m o d   7 ) 20^6 \equiv 1 (mod \, 7) 2061(mod7) 可推出 2 0 22 ≡ 2 0 4 ( m o d   7 ) 20^{22} \equiv 20^4 (mod \, 7) 2022204(mod7),即 160000 mod 7 = 1,则答案为 星期六往后数一天——星期天

方法二:代码求解

恰好整除,则证明为星期天


public class Main {
    public static void main(String[] args) {
        int ans = 1;
        for (int i = 0; i < 22; i++) {
            ans = (ans * 20) % 7;
        }
        ans = (ans + 6) % 7;
        System.out.println(ans);
    }
}

[答案]

7

试题 B: 山

1711706090900

[题解]


public class Main {
    public static void main(String[] args) {
        int ans = 0;
        for (int i = 2022; i <= 2022222022; i++) {
            String s = i + "";
            int left = 0, right = s.length() - 1;
            if (s.charAt(left) != s.charAt(right)) continue;
            left++;
            right--;
            while (left <= right) {
                if (s.charAt(left) < s.charAt(left - 1)) break;
                if (s.charAt(left) != s.charAt(right)) break;
                left++;
                right--;
            }
            if (left > right) ans++;
        }
        System.out.println(ans);
    }
}

[答案]

3138

试题 C: 字符统计

1711707938180

[题解]


import java.io.*;
/**
 * 2022	字符统计 哈希
 *
 */
public class Main {
    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pr;
    public static void main(String[] args) throws Exception{
        br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pr = new PrintWriter(new OutputStreamWriter(System.out));
        String str = br.readLine();
        char[] s = str.toCharArray();
        int[] cnt = new int[26];
        for(int i = 0; i < 26; i++) {
            cnt[i] = 0;
        }
        for(char c : s) {
            cnt[c - 'A']++;
        }
        String ans = "A";
        int max = cnt[0];
        for(int i = 1; i < 26; i++) {
            if (max < cnt[i]) {
                max = cnt[i];
                ans = "" + (char)('A' + i);
            } else if(max == cnt[i]) {
                ans += (char)('A' + i);
            }
        }
        pr.println(ans);
        pr.flush();

    }
}

试题 D: 最少刷题数

1711708716952

[题解]


import java.io.*;
import java.util.*;

/**
 * 2022	最少刷题数 模拟
 *
 */
public class Main {
    static BufferedReader br;
    static StringTokenizer st;
    static PrintWriter pw;

    public static void main(String[] args) throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        st = new StringTokenizer(br.readLine());
        pw = new PrintWriter(new OutputStreamWriter(System.out));
        new Main().go();
        pw.flush();
    }

    void go() throws IOException {
        int n = Integer.parseInt(st.nextToken());
        String str = br.readLine();
        String strArr[] = str.split(" ");
        //将 n 为 1 特判出去,表示接下来数至少有两个
        if (n == 1) {
            pw.print("0 ");
            return;
        }
        List<Integer> nums = new ArrayList<>();
        List<Integer> numsCopyed = new ArrayList<>();
        for (String s : strArr) {
            int i = Integer.parseInt(s);
            nums.add(i);
            numsCopyed.add(i);
        }
        Collections.sort(numsCopyed);
        int mid = numsCopyed.get(n / 2);
        boolean lRepeat = mid == numsCopyed.get(n / 2 - 1);
        boolean isOdd = (n & 1) == 1;
        boolean lRepeatTwo = false;
        if (!isOdd && n >= 4) {
            lRepeatTwo = mid == numsCopyed.get(n / 2 - 2);
        }
        boolean rRepeat = false;
        if (isOdd) rRepeat = mid == numsCopyed.get(n / 2 + 1);
        for (int i = 0; i < n; i++) {
            int diff = nums.get(i) - mid;

            if (diff > 0) {
                pw.print("0 ");
            } else if (diff == 0 && isOdd && lRepeat) {
                pw.print("1 ");
            } else if (diff == 0 && isOdd && !lRepeat) {
                pw.print("0 ");
            } else if (diff == 0 && !isOdd && lRepeat && lRepeatTwo) {
                pw.print("1 ");
            } else if (diff == 0 && !isOdd) {
                pw.print("0 ");
            } else if (diff < 0 && isOdd && rRepeat) {
                pw.print(-diff + " ");
            } else if (diff < 0 && isOdd && !rRepeat) {
                pw.print(-diff + 1 + " ");
            } else if (diff < 0 && !isOdd && !lRepeat) {
                pw.print(-diff + " ");
            } else if (diff < 0 && !isOdd && lRepeat) {
                pw.print(-diff + 1 + " ");
            }
        }
    }
}

试题 E: 求阶乘

1711708733112

[题解]

n! 后面有几个零,意味着求 n! 中 5 的质因子有多少个


import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;

/**
 * 2022	求阶乘 二分枚举 质因数分解
 *
 */
public class Main {
    
	static BufferedReader br;
	static PrintWriter pw;
	
	public static void main(String[] args) throws Exception{
		br = new BufferedReader(new InputStreamReader(System.in));
		pw = new PrintWriter(new OutputStreamWriter(System.out));
		new Main().go();		
		pw.flush();
	}
	
	void go() throws Exception{
		long k = Long.parseLong(br.readLine());
		long left = k, right = 5 * k;
		while(left + 1 < right) {
			long mid = left + (right - left) / 2;
			if (cal(mid) >= k) {
				right = mid;
			} else {
				left = mid;
			}
		}
		if (cal(right) != k) {
			pw.print(-1);
		} else {
			pw.print(right);
		}	
	}
	long cal(long n) {
		if (n < 5) return 0;
		return n / 5 + cal(n / 5);
	}

}

试题 F: 最大子矩阵

1711708747713

[题解]


import java.io.*;
import java.util.*;

/**
 * 2022	最大子矩阵 预处理 滑动窗口 单调队列 二维一维化
 *
 */
public class Main {

    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    public static void main(String[] args) throws Exception{
        br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));
        new Main().go();
        pw.flush();
    }
    int nextInt() throws Exception{
        st.nextToken();
        return (int)st.nval;
    }
    int n,m;
    int[][] matrix;
    int limit;
    int[][][] maxColumn;
    int[][][] minColumn;
    void go() throws Exception{
        n = nextInt();
        m = nextInt();
        matrix = new int[n][m];
        maxColumn = new int[m][n][n];
        minColumn = new int[m][n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] = nextInt();
            }
        }
        limit = nextInt();
        // 预处理维护 i 列 [j,k] 行最值
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = j; k < n; k++) {
                    if (k == j) {
                        maxColumn[i][j][k] = matrix[k][i];
                        minColumn[i][j][k] = matrix[k][i];
                    } else {
                        maxColumn[i][j][k] = Math.max(matrix[k][i], maxColumn[i][j][k - 1]);
                        minColumn[i][j][k] = Math.min(matrix[k][i], minColumn[i][j][k - 1]);
                    }
                }
            }
        }
        int ans = 1;
        for(int r1 = 0; r1 < n; r1++) {
            for(int r2 = r1; r2 < n; r2++) {
                ans = Math.max(ans, longestSubarray(r1, r2) * (r2 - r1 + 1));
            }
        }
        pw.print(ans);
    }

    // 得到行最大子矩阵,注意有可能找不到符合要求的
    int longestSubarray(int r1, int r2) {
        int l = 0, res = 0;
        // 记录窗口最小值
        Deque<Integer> minQueue = new LinkedList<>();
        // 记录窗口最大值
        Deque<Integer> maxQueue = new LinkedList<>();
        for (int i = 0; i < m; i++) {
            // 保证最小值队列单调递增
            while (!minQueue.isEmpty() && minQueue.peekLast() > minColumn[i][r1][r2]) minQueue.pollLast();
            minQueue.addLast(minColumn[i][r1][r2]);
            // 保证最大值队列单调递减
            while (!maxQueue.isEmpty() && maxQueue.peekLast() < maxColumn[i][r1][r2]) maxQueue.pollLast();
            maxQueue.addLast(maxColumn[i][r1][r2]);
            if (maxQueue.peek() - minQueue.peek() > limit) {
                while (!minQueue.isEmpty() && !maxQueue.isEmpty() && maxQueue.peek() - minQueue.peek() > limit) {
                    if (minColumn[l][r1][r2] == minQueue.peek()) minQueue.pollFirst();
                    if (maxColumn[l++][r1][r2] == maxQueue.peek()) maxQueue.pollFirst();
                }
            }
            res = Math.max(res, i - l + 1);
        }
        return res;
    }
}

试题 G: 数组切分

1711762267583

[题解]

首先明确一点:该子数组是否切分正确可通过比较该子数组的长度与该子数组的最大值 - 最小值 + 1 是否相等,若相等则切分正确,反之切分错误。

①先提供一种便于理解,但会 TLE 的回溯算法

回溯枚举了所有情况,满足条件才会 res++


import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;


/**
 * 2022 数组切分
 */
public class Main {
	
    public static void main(String[] args) throws Exception {
        new Main().go();
    }
    
    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    private void go() throws Exception {
    	br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));

        getAns();
        
        pw.flush();
    }
    
    private int nextInt() throws Exception {
		st.nextToken();
		return (int) st.nval;
	}
    
    static final int N = 10010;
    static final int MOD = (int)1e9 + 7;

    int[] a = new int[N];
    int n;

    private void getAns() throws Exception {
       n = nextInt();
       for (int i = 1; i <= n; i++) {
    	   a[i] = nextInt(); 
       }
       
       dfs(1);
       pw.println(res);
       
    }

    int res = 0;
	private void dfs(int i) {
		int max = a[i];
		int min = a[i];
		if (i == n + 1) {
			res = (res + 1) % MOD;
			return;
		}
		for (int j = i; j <= n; j++) {
			max = Math.max(max, a[j]);
			min = Math.min(min, a[j]);
			if (j - i == max - min) {
				dfs(j + 1);
			}		
		}	
	}  
}

② dp

定义状态 f(i) 为以 i 结尾所有切分合法的方法数


import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;


/**
 * 2022 数组切分
 */
public class Main {
	
    public static void main(String[] args) throws Exception {
        new Main().go();
    }
    
    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    private void go() throws Exception {
    	br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));

        getAns();
        
        pw.flush();
    }
    
    private int nextInt() throws Exception {
		st.nextToken();
		return (int) st.nval;
	}
    
    static final int N = 10010;
    static final int MOD = (int)1e9 + 7;

    int[] a = new int[N];
    int n;
    int[] f = new int[N];

    private void getAns() throws Exception {
       n = nextInt();
       for (int i = 1; i <= n; i++) {
    	   a[i] = nextInt(); 
       }
       
       f[0] = 1;
       for (int i = 1; i <= n; i++) {
    	   int max = a[i], min = a[i];
    	   for (int j = i; j <= n; j++) {
    		   max = Math.max(max, a[j]);
    		   min = Math.min(min, a[j]);
    		   if (max - min == j - i) {
    			   // 证明 [i, j] 是段合法可切分区间
    			   // 那么原先已经存在 f(i - 1) 应该加入到 f(j) 来
    			   // 那么应该更新 f(j) = f(j) + f(i - 1)
    			   f[j] = (f[j] + f[i - 1]) % MOD;
    		   }
    	   }
       }
       pw.println(f[n]);
    }
}

试题 H: 回忆迷宫

1711762304234

[题解]


import java.io.*;
import java.util.Arrays;


/**
 * 2022 回忆迷宫 模拟 dfs
 * 由于刚开始不知道,迷宫形状如何,开一个尽可能大的数组去存放迷宫
 * 存放的数组中 0 表示迷宫外围 1 表示迷宫空地 2 表示墙
 */
public class Main {

    public static void main(String[] args) throws Exception {
        new Main().go();
    }

    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    private void go() throws Exception {
        br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));

        getAns();

        pw.flush();
    }

    private int nextInt() throws Exception {
        st.nextToken();
        return (int) st.nval;
    }

    static final int N = 110;
    int[][] G = new int[2 * N + 1][2 * N + 1];
    int xMin, yMin;
    int row, column;

    private void getAns() throws Exception {
        int n = Integer.valueOf(br.readLine());
        String s = br.readLine();
        br.close();
        s = " " + s;
        char[] str = s.toCharArray();

        // 迷宫空地
        int x = N + 1, y = N + 1;
        G[x][y] = 1;
        for (int i = 1; i <= n; i++) {
            char c = str[i];
            if (c == 'U') {
                x--;
                G[x][y] = 1;
            } else if (c == 'D') {
                x++;
                G[x][y] = 1;
            } else if (c == 'L') {
                y--;
                G[x][y] = 1;
            } else if (c == 'R') {
                y++;
                G[x][y] = 1;
            }
        }

        // 迷宫的墙
        for (int i = N + 1 - n; i <= N + 1 + n; i++) {
            for (int j = N + 1 - n; j <= N + 1 + n; j++) {
                if (G[i][j] == 1) {
                    if (G[i - 1][j] != 1) {
                        G[i - 1][j] = 2;
                    }
                    if (G[i + 1][j] != 1) {
                        G[i + 1][j] = 2;
                    }
                    if (G[i][j - 1] != 1) {
                        G[i][j - 1] = 2;
                    }
                    if (G[i][j + 1] != 1) {
                        G[i][j + 1] = 2;
                    }
                }
            }
        }
        // 找到应该迷宫所在的位置
        xMin = Integer.MAX_VALUE;
        yMin = Integer.MAX_VALUE; // 迷宫出现的最小行和列数
        int xMax = Integer.MIN_VALUE, yMax = Integer.MIN_VALUE;   // 迷宫最大行、列数
        for (int i = N - n; i <= N + 2 + n; i++) {
            int xFirst = -1, yFirst = -1;
            boolean isFirst = true;
            int yLast = -1;
            for (int j = N - n; j <= N + 2 + n; j++) {
                if (G[i][j] == 2) {
                    yLast = j;
                    if (isFirst) {
                        yFirst = j;
                        xFirst = i;
                        isFirst = false;
                    }
                }
            }
            if (!isFirst) {
                xMin = Math.min(xMin, xFirst);
                yMin = Math.min(yMin, yFirst);
                xMax = Math.max(xMax, xFirst);
                yMax = Math.max(yMax, yLast);
            }
        }

        row = xMax - xMin + 1;
        column = yMax - yMin + 1;
        // 将需要判断连通区域设置的大于原来的实际迷宫区域,保证迷宫空地都在一个连通块内
        width = row + 2;
        length = column + 2;
        matrix = new int[width][length];
        vis = new boolean[width][length];
        for (int[] ints : matrix) {

            Arrays.fill(ints, 0);
        }
        for (boolean[] vi : vis) {
            Arrays.fill(vi, false);
        }
        // 由于上面的填充墙,只会围绕空地四周填充,内部实心墙不会填充
        // 下面就是将剩余为填充的墙,填充完整
        // 由于空地将整个区域化为两个连通块并且迷宫外围的连通块一定是率先遍历到的,我们这个连通块不要,剩下连通块都填充即可
        wall();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                if (G[xMin + i][yMin + j] == 0 && matrix[i + 1][j + 1] != 1)
                    G[xMin + i][yMin + j] = 2;
            }
        }
        // 打印结果
        for (int i = xMin, k = 0; i <= xMax; i++, k++) {
            for (int j = yMin; j <= yMax; j++) {
                if (G[i][j] == 1 || G[i][j] == 0) {
                    pw.print(" ");
                } else if (G[i][j] == 2) {
                    pw.print("*");
                }

            }
            pw.println();
        }
    }

    int length, width;
    int[][] matrix;
    boolean[][] vis;
    int order;

    private void wall() {
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < length; j++) {
                if (G[xMin + i - 1][yMin + j - 1] == 0 &&!vis[i][j]) {
                    order++;
                    dfs(i, j);
                }
            }
        }
    }

    private void dfs(int x, int y) {
        if (x + 1 > width || x < 0 || y + 1 > length || y < 0 || vis[x][y]) return;
        vis[x][y] = true;
        if (G[xMin + x - 1][yMin + y - 1] != 0) return;
        matrix[x][y] = order;
        dfs(x + 1, y);
        dfs(x - 1, y);
        dfs(x, y + 1);
        dfs(x, y - 1);
    }
}

试题 I: 红绿灯

1711762333406

[题解]


import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;


/**
 * 2022 红绿灯 dp
 * f(x, y) 表示到达第 x 个地方, 使用氮气加速需要冷却的红绿灯个数 y 特别记 x = 0 为起点, x = m + 1 为终点 
 */
public class Main {
	
    public static void main(String[] args) throws Exception {
        new Main().go();
    }
    
    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    private void go() throws Exception {
    	br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));

        getAns();
        
        pw.flush();
    }
    
    private int nextInt() throws Exception {
		st.nextToken();
		return (int) st.nval;
	}
    
    static final int N = (int)10e8 + 10, M = 1010;
    int n, m, k, v;	// n 家到公司的距离 m 红绿灯个数 k 加速间隔的红绿灯个数 v 速度的倒数
    long[] pos = new long[M], green = new long[M], red = new long[M];	// pos 起点、红绿灯、终点所在位置 green 绿灯持续时间 red 红灯持续时间 
    long[][] f = new long[M][M];
    private void getAns() throws Exception {
        n = nextInt();
        m = nextInt();
        k = nextInt();
        v = nextInt();
        pos[0] = 0;
        pos[m + 1] = n;
        for (int i = 1; i <= m; i++) {
			pos[i] = nextInt();
			green[i] = nextInt();
			red[i] = nextInt();
		}
        
        f[0][0] = 0;
        for (int i = 1; i <= k - 1; i++) {
        	// 定义非法数值取得要恰当些,要不然可能下面计算会导致数据溢出而使得答案错误
			f[0][i] = Integer.MAX_VALUE;
		}
        for (int i = 1; i <= m + 1; i++) {
        	long intervalTime = (pos[i] - pos[i - 1]) * v;
        	// f[i][0] 由能开瞬移但不开瞬移和不能开瞬移取小转移得来
			f[i][0] = intervalTime + Math.min(f[i - 1][0] + waitTime(i, f[i - 1][0] + intervalTime),
											  f[i - 1][1] + waitTime(i, f[i - 1][1] + intervalTime));
			// j > 0 表示没法开瞬移,即 f[i][j] 由 f[i - 1][j + 1] 转移得到
			for(int j = 1; j <= k - 2; j++) {
				f[i][j] = f[i - 1][j + 1] + intervalTime + waitTime(i, f[i - 1][j + 1] + intervalTime);
			}
			// 开瞬移
			f[i][k - 1] =  f[i - 1][0] + waitTime(i, f[i - 1][0]);
		}
        long ans = Long.MAX_VALUE;
        for (int i = 0; i <= k - 1; i++) {
			ans = Math.min(ans, f[m + 1][i]);
		}
        pw.println(ans);

    }
    
    /**
     * 计算出在红绿灯该等多久
     * @param i
     * @param time
     * @return
     */
	private long waitTime(int i, Long time) {
		if (i > m) return 0;
		// time % (green[i] + red[i]) 得到的值是该轮次红绿灯走的时间,若恰好等于 green[i] 说明红灯刚好亮
		return time % (green[i] + red[i]) < green[i] ? 0 : red[i] - (time % (green[i] + red[i]) - green[i]);
	}   
}

试题 J: 拉箱子

1711762374293

[题解]


import java.io.*;
import java.util.*;


/**
 * 2022 拉箱子 模拟
 */
public class Main {

    public static void main(String[] args) throws Exception {
        new Main().go();
    }

    static BufferedReader br;
    static StreamTokenizer st;
    static PrintWriter pw;

    private void go() throws Exception {
        br = new BufferedReader(new InputStreamReader(System.in));
        st = new StreamTokenizer(br);
        pw = new PrintWriter(new OutputStreamWriter(System.out));

        getAns();

        pw.flush();
    }

    private int nextInt() throws Exception {
        st.nextToken();
        return (int) st.nval;
    }

    static final int N = 15;
    int n, m;
    int[][] grid = new int[N][N];
    // 存储空地的坐标
    List<Integer> list = new ArrayList<>();
    int[] dx = {0, 1, 0, -1}, dy = {1, 0, -1, 0};
    int[][][] arr = new int[N * N][4][2];

    private void getAns() throws Exception {
        n = nextInt();
        m = nextInt();
        for (int i = 1, idx = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                grid[i][j] = nextInt();
                if (grid[i][j] == 0) {
                    grid[i][j] = idx++;
                    list.add(i * 100 + j);
                } else if (grid[i][j] == 1) {
                    // -1 代表非法位置, 即这里有墙
                    grid[i][j] = -1;
                }
            }
        }
        int len = list.size();
        for (int i = 1; i <= len; i++) {
            // 初始化并查集
            int[] father = new int[len + 1];
            int[] size = new int[len + 1];
            for (int j = 1; j <= len; j++) {
                father[j] = j;
                size[j] = 1;
            }
            // 合并连通区域
            for (int j = 1; j <= len; j++) {
                if (i == j) continue;
                int x = list.get(j - 1) / 100, y = list.get(j - 1) % 100;
                for (int k = 0; k < 2; k++) {
                    int nx = x + dx[k], ny = y + dy[k];
                    // 越界或无意义
                    if (nx < 1 || ny < 1 || nx > n || ny > m || grid[nx][ny] == -1 || grid[nx][ny] == i) continue;
                    int fa = find(father, j), fb = find(father, grid[nx][ny]);
                    if (fa != fb) {
                        father[fa] = fb;
                        size[fb] += size[fa];
                    }
                }
            }
            // 得到一个点四周的连通情况
            int x = list.get(i - 1) / 100, y = list.get(i - 1) % 100;
            for (int j = 0; j < 4; j++) {
                int nx = x + dx[j], ny = y + dy[j];
                if (nx < 1 || ny < 1 || nx > n || ny > m || grid[nx][ny] == -1) continue;
                int fx = find(father, grid[nx][ny]);
                arr[i][j] = new int[]{fx, size[fx]};
            }
        }
        int ans = 0;
        for (int target = 1; target <= len; target++) {
            int tx = list.get(target - 1) / 100, ty = list.get(target - 1) % 100;
            for (int box = 1; box <= len; box++) {
                if (box == target) continue;
                int bx = list.get(box - 1) / 100, by = list.get(box - 1) % 100;
                Set<Integer> set = new HashSet<>();
                for (int i = 0; i < 4; i++) {
                    int nx = bx + dx[i], ny = by + dy[i];
                    if (nx < 1 || ny < 1 || nx > n || ny > m || grid[nx][ny] == -1) continue;
                    int block = arr[box][i][0];
                    if (!set.contains(block) && bfs(tx, ty, bx, by, i)) {
                        set.add(block);
                        ans += arr[box][i][1];
                        // 判断人是否有可能占据了终点的位置
                        if (isConnected(bx, by, tx, ty, nx, ny)) ans--;
                    }
                }
            }
        }
        pw.println(ans);
    }

    private boolean isConnected(int bx, int by, int tx, int ty, int fx, int fy) {
        boolean[][] vis = new boolean[n + 1][m + 1];
        Queue<int[]> queue = new ArrayDeque<>();
        queue.add(new int[]{fx, fy});
        while (!queue.isEmpty()) {
            int[] tmps = queue.poll();
            int x = tmps[0], y = tmps[1];
            if (x == tx && y == ty) return true;
            for (int i = 0; i < 4; i++) {
                int nx = x + dx[i], ny = y + dy[i];
                if (nx < 1 || ny < 1 || nx > n || ny > m || (bx == nx && by == ny) || grid[nx][ny] == -1 || vis[nx][ny])
                    continue;
                vis[nx][ny] = true;
                queue.add(new int[]{nx, ny});
            }
        }
        return false;
    }

    // 判断这个位置的箱子能否到达终点
    private boolean bfs(int tx, int ty, int bx, int by, int dir) {
        boolean[][][] vis = new boolean[n + 1][m + 1][4];
        Queue<int[]> queue = new ArrayDeque<>();
        queue.add(new int[]{bx, by, dir});
        vis[bx][by][dir] = true;
        while (!queue.isEmpty()) {
            int[] tmps = queue.poll();
            int x = tmps[0], y = tmps[1], d = tmps[2];
            if (x == tx && y == ty) return true;
            // 直走
            // 箱子的下一个位置
            int nx = x + dx[d], ny = y + dy[d];
            if (nx + dx[d] > 0 && nx + dx[d] <= n && ny + dy[d] > 0 && ny + dy[d] <= m    // 人不出界
                    && grid[nx + dx[d]][ny + dy[d]] != -1 // 人的位置不是墙
                    && !vis[nx][ny][d]) {
                vis[nx][ny][d] = true;
                // 箱子合法位置入队
                queue.add(new int[]{nx, ny, d});
            }

            int b = arr[grid[x][y]][d][0];
            // 转弯
            for (int i = 0; i < 4; i++) {
                if (i == d) continue;
                // 满足人可以到达箱子要转弯的位置,才合法
                if (arr[grid[x][y]][i] != null && arr[grid[x][y]][i][0] == b && !vis[x][y][i]) {
                    vis[x][y][i] = true;
                    // 满足转弯条件入队
                    queue.add(new int[]{x, y, i});
                }
            }

        }
        return false;
    }

    private int find(int[] father, int x) {
        if (father[x] == x) return x;
        int fx = find(father, father[x]);
        father[x] = fx;
        return fx;
    }

}

以上综合各位大佬提供给的思路和我自己的思路,自己写出的题解。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
蓝桥杯是一个国内著名的计算机比赛,为了帮助参赛者更好地准备和了解比赛的题型,委会会公布历年的真题并提供相应的题解。 首先,我们需要了解蓝桥杯是一个综合性的计算机比赛,测试的对象包括计算机基础知识、编程能力以及解决实际问题的能力。 在历年的真题中,参赛者将面临不同类型的题目,包括算法设计与优化问题、数据结构与算法问题、编程题等。其中针对Python B的题目主要考察的是对Python语言的掌握和应用能力。 题目解答一般会包含以下几个方面的内容: 1. 题目分析与理解:读取题目,理解题目的要求和限制条件。通过仔细分析题目,确定题目的输入与输出,以及问题的核心。 2. 设计解决方案:根据题目要求和限制条件,设计一个合适的解决方案。可以使用合适的算法和数据结构来解决问题,并做出相应的性能优化。 3. 编写代码实现:根据设计的方案编写相应的代码实现。需要注意的是,Python语言有其独特的语法和特性,掌握好这些特性可以更好地完成编程任务。 4. 调试与测试:编写完代码后,需要进行调试和测试。通过运行样例输入和输出,检查代码是否符合题目要求,并且没有逻辑上的错误。 5. 总结与优化:在完成题目解答后,可以进行总结和优化。包括分析算法复杂度、代码风格和可读性等方面,以便在比赛中更好地表现。 在准备蓝桥杯时,可以通过阅读历年的真题题解来了解比赛的难度和类型,针对性地进行练习和提高。同时也可以参加相关的培训班和讨论活动,与其他参赛者交流经验和技巧。 总而言之,历年蓝桥杯真题的解答对于提高自己的编程能力和应对比赛非常有帮助。通过认真分析和实践,可以更好地理解并掌握Python编程,并在比赛中取得更好的成绩。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值