[国一]2024计算机能力挑战赛Java赛项决赛(5道编程大题+题解思路解析)

提交说明:
1.请严格按照每道题目给出的输入/输出样例编写相关I/O代码,数字间的默认间隔是一个空格,样例以外的提示信息请不要在屏幕上输出。
2.请大家确保提交的代码可以在指定的编译条件下正确地编译执行,否则自动评测程序将给出编译错误或运行时错误的信息。
3.每道编程题会有多个测试用例,每通过一些测试用例可以获得相应的分值,但只有通过全部测试用例才能拿到这题全部的分数。
4.代码须点击提交,以最后一次提交为评审依据。
5.C语言请选择"C" , C++请选择"C++" , java请选择"java11" , python请选择" python3"。
6.Python程序仅可以使用Python自带的库,评测时不会安装其他的扩展库。
7.java类名请使用Main,主函数请使用main命名。注意:Java 程序源代码中不应指定所在的 package。我们会在源代码中找到第一个被定义的类并以它的 main 函数为程序入口点。
8.请不要使用 system("pause"),sleep()等一系列暂停程序运行的函数。
9.程序中应只包含计算模块,不要包含任何其他的模块,比如图形、系统接口调用、系统中断等。对于系统接口的调用都应通过标准库来进行。
10.程序中引用的库应该在程序中以源代码的方式写出,在提交时也应当和程序的其他部分一起提交。
1.按数位和分组的最大组统计
给你一个整数 n 。请你先求出从 1 到 n 的每个整数 10 进制表示下的数位和(每一位上的数字相加),然后把数位和相等的数字放到同一个组中。请你统计每个组中的数字数目,并返回数字数目并列最多的组有多少个。
输入要求:
输入总计一行。输入一个正整数 n,表示分析的范围从 1 到 n。输入范围:1 <= n <= 99999。
输出要求:
输出一个整数,表示数位和分组中,包含最多数字的组的数量。
示例一:
输入:
13
输出:
4
示例说明:
总共有 9 个组,将 1 到 13 按数位求和后这些组分别是: [1,10](数位和为1),[2,11] (数位和为2),[3,12] (数位和为3),[4,13] (数位和为4),[5] (数位和为5),[6] (数位和为6),[7] (数位和为7),[8] (数位和为8),[9] (数位和为9)。总共有 4 个组拥有的数字并列最多。
示例二:
输入:
2
输出:
2
import java.util.HashMap;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 创建一个 Scanner 对象用于读取用户输入
Scanner inputReader = new Scanner(System.in);
// 读取用户输入的整数 n
int upperLimit = inputReader.nextInt();
// 创建哈希表用于存储每个数字和及其出现的次数
HashMap<Integer, Integer> sumFrequency = new HashMap<>();
// 遍历从1到upperLimit的所有整数
for (int num = 1; num <= upperLimit; num++) {
// 计算当前整数num的各位数字之和
int digitSum = calculateDigitSum(num);
// 更新哈希表中该digitSum的出现频率
sumFrequency.put(digitSum, sumFrequency.getOrDefault(digitSum, 0) + 1);
}
// 初始化最大频率和具有该频率的数字和的数量
int maxFreq = 0;
int maxFreqCount = 0;
// 遍历哈希表中的所有频率值
for (Integer freq : sumFrequency.values()) {
if (freq > maxFreq) {
// 如果找到更大的频率,则更新最大频率,并重置计数为1
maxFreq = freq;
maxFreqCount = 1;
} else if (freq == maxFreq) {
// 如果频率等于当前最大频率,则增加计数
maxFreqCount++;
}
}
// 输出具有最高频率的数字和的数量
System.out.print(maxFreqCount);
// 关闭Scanner对象
inputReader.close();
}
/**
* 计算给定整数num的各位数字之和
* @param num 要计算的整数
* @return 各位数字之和
*/
private static int calculateDigitSum(int num) {
int sum = 0;
while (num > 0) {
sum += num % 10; // 取得最后一位并加到sum上
num /= 10; // 去掉最后一位
}
return sum;
}
}
代码详细思路解析
这段代码的目的是解决一个特定的问题:对于给定范围内的所有整数(从1到n),根据它们的各位数字之和进行分组,然后找出拥有最多成员的组,并返回这样的组的数量。
主函数 main 方法
- 读取用户输入:
-
- 使用
Scanner类创建对象inputReader来读取用户的输入。 - 通过调用
nextInt()方法读取用户输入的一个整数upperLimit,这个值表示我们要分析的范围是从1到upperLimit。
- 使用
- 初始化数据结构:
-
- 创建了一个
HashMap<Integer, Integer>类型的变量sumFrequency。这个哈希表用来存储每个可能的“数位和”作为键,以及该数位和出现的次数作为值。
- 创建了一个
- 遍历并计算数位和:
-
- 使用
for循环迭代从1到upperLimit的所有整数。 - 对于每一个整数
num,调用辅助方法calculateDigitSum(num)来计算它的数位和。 - 更新哈希表
sumFrequency,如果该数位和已经存在于哈希表中,则其对应的值(出现次数)加1;否则,将该数位和作为新键加入哈希表,并设置初始值为1。
- 使用
- 寻找最大频率:
-
- 初始化两个变量:
maxFreq用于存储当前遇到的最大频率,maxFreqCount用于记录达到此最大频率的数位和的数量。 - 遍历哈希表
sumFrequency中的所有值(即各个数位和的出现次数)。如果当前频率大于maxFreq,则更新maxFreq并重置maxFreqCount为1(因为我们找到了一个新的最大频率)。如果当前频率等于maxFreq,则增加maxFreqCount,因为又找到了一个具有相同最大频率的数位和。
- 初始化两个变量:
- 输出结果:
-
- 最后,程序打印出
maxFreqCount,即具有最高频率的数位和的数量。
- 最后,程序打印出
- 清理资源:
-
- 关闭
Scanner对象inputReader以释放资源。
- 关闭
辅助方法 calculateDigitSum
- 这个静态方法接收一个整数
num作为参数,并返回该整数的各位数字之和。 - 它使用一个
while循环不断提取num的最后一位数字(通过num % 10),将其累加到变量sum中,然后去掉最后一位(通过num /= 10),直到num变为0。 - 当
num不再大于0时,方法返回累加的结果sum,即num的数位和。
2.环路加油站问题
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号;否则返回 -1。请输出加油站编号,编号从0开始。
输入要求:
输入总计3行。第一行为一个整数n,表示加油站的个数;第二行为n个加油站中的汽油升数;第三行为从当前加油站到下一个加油站需要消耗的汽油升数。
• 0<n<100000
• 0<=汽油升数<=10000
输出要求:
输出总计一行。如果可以绕环路行驶一周,输出出发加油站的编号(存在多个出发加油站情况下,则输出较小的加油站编号);如果无法绕环路行驶一周,输出 -1。
示例一:
输入:
5
1 2 3 4 5
3 4 5 1 2
输出:
3
示例说明:
假设从编号0加油站出发,只有1升汽油,而前往下一加油站需要3升汽油,因此无法抵达;
假设从编号1加油站出发,只有2升汽油,而前往下一加油站需要4升汽油,因此无法抵达。
假设从编号2加油站出发,只有3升汽油,而前往下一加油站需要5升汽油,因此无法抵达。
假设从编号3加油站出发,则可顺序循环一周。因此输出加油站编号3。
示例二:
输入:
3
2 3 4
3 4 3
输出:
-1
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 创建一个 Scanner 对象用于读取输入
Scanner scanner = new Scanner(System.in);
// 读取加油站的数量
int stationCount = scanner.nextInt();
// 初始化两个数组分别存储每个加油站的汽油量和到下一个加油站的成本
int[] gasAmounts = new int[stationCount];
int[] costsToNext = new int[stationCount];
// 读取每个加油站的汽油量
for (int i = 0; i < stationCount; i++) {
gasAmounts[i] = scanner.nextInt();
}
// 读取从每个加油站到下一个加油站的成本
for (int i = 0; i < stationCount; i++) {
costsToNext[i] = scanner.nextInt();
}
// 调用方法计算能否完成一圈,并打印结果
System.out.println(canCompleteCircuit(gasAmounts, costsToNext));
// 关闭 Scanner 对象以释放资源
scanner.close();
}
/**
* 判断是否可以从某个起点出发,绕环路一圈。
* @param gasAmounts 每个加油站可以获得的汽油量
* @param costsToNext 从每个加油站到下一个站点的成本
* @return 如果可以完成一圈,则返回起始站的索引;否则返回 -1
*/
private static int canCompleteCircuit(int[] gasAmounts, int[] costsToNext) {
int totalGas = 0, totalCost = 0;
int currentFuel = 0, startStation = 0;
// 遍历所有加油站
for (int i = 0; i < gasAmounts.length; i++) {
// 累计总的汽油量和总成本
totalGas += gasAmounts[i];
totalCost += costsToNext[i];
// 计算当前剩余油量(即从起始站开始到当前站后还剩多少油)
currentFuel += gasAmounts[i] - costsToNext[i];
// 如果当前剩余油量小于零,说明从起始站不能到达当前站
// 因此更新起始站为下一站,并重置当前剩余油量为0
if (currentFuel < 0) {
startStation = i + 1;
currentFuel = 0;
}
}
// 如果总的汽油量大于或等于总成本,返回起始站的索引;否则返回-1表示无解
return totalGas >= totalCost ? startStation : -1;
}
}
思路解析
- 初始化:我们首先需要知道有多少个加油站,然后根据这个数量来创建两个数组,分别用来保存每个加油站的汽油量和到下一个加油站的成本。
- 读取数据:通过循环读取用户输入的数据,填充到
gasAmounts和costsToNext数组中。 - 核心算法:
-
- 我们遍历所有的加油站,同时跟踪几个关键变量:
-
-
totalGas:所有加油站提供的总汽油量。totalCost:环绕一圈所需支付的总成本。currentFuel:从起始点到现在位置为止剩余的汽油量。startStation:潜在的起始加油站的索引。
-
-
- 在遍历过程中,如果发现
currentFuel小于零,这意味着从当前的startStation出发无法到达当前位置,因此我们需要更新startStation为下一个加油站,并重置currentFuel。
- 在遍历过程中,如果发现
- 判断是否有解:在遍历完成后,我们检查
totalGas是否大于或等于totalCost。如果是,则说明存在一个解,返回startStation的值;如果不是,则返回-1表示没有解。 - 结束:最后关闭
Scanner对象,确保程序正确地释放了资源。
3.天际线问题、
城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回由这些建筑物形成的天际线。每个建筑物的几何信息由数组buildings表示,其中三元组buildings[i]=[lefti, righti, heighti]表示:
•lefti是第i座建筑物左边缘的x坐标。
•righti是第i座建筑物右边缘的x坐标。
•heighti是第i座建筑物的高度。
你可以假设所有的建筑都是完美的长方形,在高度为0的绝对平坦的表面上。
天际线应该表示为由“关键点”组成的列表,格式 [[x1,y1],[x2,y2],...] ,并按x坐标进行排序。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y坐标始终为0,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
注意:输出天际线中不得有连续的相同高度的水平线。例如[...[2 3], [4 5], [7 5], [11 5], [12 7]...]是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]。
输入要求:
输入总计n+1行。第一行为建筑物个数n;后n行每一行为建筑物的几何信息三元组,三元组内元素使用空格分隔。
输出要求:
输出总计一行。输出一个二维数组,即表示天际线的“关键点”数组。
示例一:
输入:
5
2 9 10
3 7 15
5 12 12
15 20 10
19 24 8
输出:
[[2, 10], [3, 15], [7, 12], [12, 0], [15, 10], [20, 8], [24, 0]]
示例说明:
见下图:图 A 显示输入的所有建筑物的位置和高度;图 B 显示由这些建筑物形成的天际线。图 B 中的红点表示输出列表中的关键点。

示例二:
输入:
2
0 2 3
2 5 3
输出:
[[0, 3], [5, 0]]
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.TreeMap;
public class SkylineProblem {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取建筑物数量
int buildingCount = scanner.nextInt();
List<int[]> buildings = new ArrayList<>();
// 读取每个建筑物的左边界、右边界和高度
for (int i = 0; i < buildingCount; i++) {
buildings.add(new int[]{scanner.nextInt(), scanner.nextInt(), scanner.nextInt()});
}
// 获取天际线轮廓
int[][] skyline = getSkyline(buildings);
// 打印天际线轮廓
printSkyline(skyline);
scanner.close();
}
/**
* 计算天际线轮廓的关键点。
* @param buildings 建筑物列表,每个建筑物由 [左边界, 右边界, 高度] 表示
* @return 天际线轮廓的关键点,每个点由 [x坐标, y坐标] 表示
*/
private static int[][] getSkyline(List<int[]> buildings) {
List<int[]> events = new ArrayList<>(); // 存储所有的事件(高度变化)
// 将每个建筑物转化为两个事件:高度增加和高度减少
for (int[] building : buildings) {
events.add(new int[]{building[0], -building[2]}); // 左边界的高度增加
events.add(new int[]{building[1], building[2]}); // 右边界的高度减少
}
// 按照 x 坐标排序,若 x 相同则高度增加事件优先
events.sort((a, b) -> a[0] != b[0] ? a[0] - b[0] : Integer.compare(a[1], b[1]));
TreeMap<Integer, Integer> heightCounts = new TreeMap<>(); // 当前高度及其出现次数
heightCounts.put(0, 1); // 初始化为地面高度
int prevMaxHeight = 0;
List<int[]> result = new ArrayList<>(); // 存储天际线的关键点
// 处理每一个事件
for (int[] event : events) {
if (event[1] < 0) { // 高度增加事件
heightCounts.put(-event[1], heightCounts.getOrDefault(-event[1], 0) + 1);
} else { // 高度减少事件
int count = heightCounts.get(event[1]);
if (count == 1) {
heightCounts.remove(event[1]);
} else {
heightCounts.put(event[1], count - 1);
}
}
int currMaxHeight = heightCounts.lastKey(); // 更新当前最大高度
// 如果最大高度发生了改变,则记录新的关键点
if (currMaxHeight != prevMaxHeight) {
result.add(new int[]{event[0], currMaxHeight});
prevMaxHeight = currMaxHeight;
}
}
return result.toArray(new int[result.size()][]);
}
/**
* 格式化并打印天际线轮廓。
* @param skyline 天际线轮廓的关键点
*/
private static void printSkyline(int[][] skyline) {
StringBuilder output = new StringBuilder("[");
for (int i = 0; i < skyline.length; i++) {
output.append("[").append(skyline[i][0]).append(",").append(skyline[i][1]).append("]");
if (i < skyline.length - 1) {
output.append(",");
}
}
output.append("]");
System.out.println(output.toString());
}
}
4.拼接最大数
给你两个整数数组nums1和nums2,它们的长度分别为m和n。数组nums1和nums2分别代表两个数各位上的数字。同时你也会得到一个整数k。请你利用这两个数组中的数字中创建一个长度为k<=m+n的最大数,且必须保留来自同一数组的数字的相对顺序。最后,输出代表答案的长度为 k 的数组。
输入要求:
输入总计4行。第一行输入m和n;第二行输入数组nums1的m个元素;第三行输入数组nums2的n个元素;第四行输入k。输入元素之间使用空格分隔。
输出要求:
输出总计一行。输出答案数组。输出元素之间使用空格分隔。
示例一:
输入:
4 6
3 4 6 5
9 1 2 5 8 3
5
输出:
9 8 6 5 3
示例说明:
依次从数组二取出9,8,数组一取出6,5,数组二取出3组成的数98653是k=5限定下的最大数。
示例二:
输入:
2 3
6 7
6 0 4
5
输出:
6 7 6 0 4
import java.util.Scanner;
public class MaxNumberCombination {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 读取输入的两个数组长度及k值
int len1 = input.nextInt();
int len2 = input.nextInt();
int[] arr1 = new int[len1];
int[] arr2 = new int[len2];
for (int i = 0; i < len1; i++) {
arr1[i] = input.nextInt();
}
for (int i = 0; i < len2; i++) {
arr2[i] = input.nextInt();
}
int k = input.nextInt(); // 需要选择的总数字数
input.close();
// 计算最大组合
int[] result = findMaxCombination(arr1, arr2, k);
// 打印结果
StringBuilder output = new StringBuilder();
for (int num : result) {
output.append(num).append(" ");
}
if (output.length() > 0) {
output.setLength(output.length() - 1); // 移除最后一个多余的空格
}
System.out.println(output.toString());
}
/**
* 寻找两个数组中能够组成的最大k位数。
*/
private static int[] findMaxCombination(int[] arr1, int[] arr2, int k) {
int[] bestResult = new int[k];
// 尝试所有可能的分割方式,从arr1中选取i个元素,从arr2中选取k-i个元素
for (int i = Math.max(0, k - arr2.length); i <= Math.min(k, arr1.length); i++) {
int[] part1 = selectMaxSequence(arr1, i);
int[] part2 = selectMaxSequence(arr2, k - i);
int[] candidate = mergeSequences(part1, part2);
if (isGreater(candidate, 0, bestResult, 0)) {
bestResult = candidate;
}
}
return bestResult;
}
/**
* 从数组中选择指定数量的最大数字序列,保持其相对顺序。
*/
private static int[] selectMaxSequence(int[] nums, int count) {
int[] selected = new int[count];
int selIndex = 0;
int dropCount = nums.length - count;
for (int num : nums) {
// 当有剩余删除机会,且当前选中的序列尾部小于新元素时,移除尾部较小元素
while (dropCount > 0 && selIndex > 0 && selected[selIndex - 1] < num) {
selIndex--;
dropCount--;
}
if (selIndex < count) {
selected[selIndex++] = num;
} else {
dropCount--; // 如果已选够,则减少可丢弃的数量
}
}
return selected;
}
/**
* 合并两个部分,生成尽可能大的序列。
*/
private static int[] mergeSequences(int[] seq1, int[] seq2) {
int[] merged = new int[seq1.length + seq2.length];
int idx1 = 0, idx2 = 0, mergedIdx = 0;
while (idx1 < seq1.length || idx2 < seq2.length) {
// 比较两个序列剩余部分,优先添加较大的元素到合并序列
if (idx2 == seq2.length || (idx1 < seq1.length && isGreater(seq1, idx1, seq2, idx2))) {
merged[mergedIdx++] = seq1[idx1++];
} else {
merged[mergedIdx++] = seq2[idx2++];
}
}
return merged;
}
/**
* 比较两个数组从特定索引开始的部分,判断哪一个部分更大。
*/
private static boolean isGreater(int[] seq1, int idx1, int[] seq2, int idx2) {
// 如果两个序列在当前位置相等,则继续比较后续元素
while (idx1 < seq1.length && idx2 < seq2.length && seq1[idx1] == seq2[idx2]) {
idx1++;
idx2++;
}
// 如果一个序列已经遍历完或者另一个序列的当前元素更大,则返回false
return idx2 == seq2.length || (idx1 < seq1.length && seq1[idx1] > seq2[idx2]);
}
}
思路解析
- 读取数据:程序首先读取用户输入的数据,包括两个数组
arr1和arr2以及需要构建的最大数的长度k。 - 寻找最大组合:
-
findMaxCombination函数尝试不同的分割方法,即从arr1中选取i个元素,从arr2中选取k-i个元素。对于每种分割,它调用selectMaxSequence来获取每个数组中可以形成的最大序列。- 然后,它通过
mergeSequences将这两个序列合并成一个新的序列,并使用isGreater函数检查这个新序列是否比目前找到的最佳结果更好。如果是,则更新最佳结果。
- 选择最大序列:
selectMaxSequence函数用于从给定的数组中挑选出指定数量的数字,以形成该数组内的最大可能序列。此过程遵循贪心算法原则,确保所选序列是局部最优解(即对于每一个位置,尽可能选择更大的数字)。 - 合并序列:
mergeSequences负责将两个局部最优解合并为一个全局最优解。它总是选择当前最大的可用元素加入最终结果,从而保证最终结果尽可能大。 - 比较序列:
isGreater函数用来比较两个序列的大小,它不仅比较第一个不同的元素,还会继续比较后续元素,直到分出高低,以确保选择的序列是真正意义上的最大。 - 输出结果:最后,程序将计算得到的最大组合打印出来。为了格式化输出,程序会去除末尾多余的空格。
5.围绕区域捕获
在一个棋盘上,玩家需要捕获某些被围绕的区域。棋盘上的每个位置要么是 'X',要么是 'O',玩家的任务是将所有被 'X' 围绕的 'O' 区域标记为 'X',而没有被围绕的 'O' 区域保持不变。
具体来说,若 'O' 连接到棋盘的边缘,或者可以通过水平或垂直方向的 'O' 连接到边缘,则该区域不能被围绕,因此要保留。而其他完全被 'X' 围绕的区域应该被转变成 'X'。
输入要求:
输入总计m+1行。第一行为棋盘的行数m和列数n;后m行中,每一行包含n个元素,输入元素之间使用空格分隔,组成一个 m x n 的矩阵 board,由字符 'X' 和 'O' 组成,其中:
• m = board.length(行数),n = board[i].length(列数)
• 1 <= m, n <= 200
• board[i][j] 为 'X' 或 'O'。
输出要求:
输出总计m行。每一行包含n个元素,输出元素之间使用空格分隔,即输出一个修改后的 board,其中所有被 'X' 围绕的 'O' 区域被转变为 'X'。
示例一:
输入:
4 4
X X X X
X O O X
X X O X
X O X X
输出:
X X X X
X X X X
X X X X
X O X X
示例说明:
第二行位于下标1和2处的O和第三行位于下标2处的O无法通过其他水平或垂直方向的O与矩阵边界连通,因此被包围,替换为X。
示例二:
输入:
1 1
X
输出:
X
思路解析
- 问题理解:
-
- 目标是翻转所有被 'X' 完全包围的 'O' 区域。
- 但是,任何与边界相连的 'O'(直接或间接)都不应被翻转。
- 解决方案:
-
- 使用深度优先搜索(DFS)从棋盘的四个边界开始,找到所有与边界相连的 'O'。
- 标记这些 'O' 以防止它们被翻转。
- 遍历整个棋盘,翻转未标记的 'O',并还原标记的 'O'。
- 具体步骤:
-
- 读取输入:获取棋盘尺寸和初始状态。
- 标记不可翻转的 'O':使用 DFS 从棋盘边界开始,遍历与边界相连的 'O' 并标记。
- 更新棋盘:遍历棋盘,根据标记来决定是否翻转 'O'。
- 输出结果:打印最终的棋盘状态。
import java.util.Scanner;
public class Main {
private static final char FREE = 'O'; // 棋盘上自由的 O
private static final char BLOCKED = 'X'; // 棋盘上的 X 或者被包围的 O
private static final char MARKED = 'M'; // 与边界相连的 O 的临时标记
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取棋盘尺寸
int rows = scanner.nextInt(); // 行数
int cols = scanner.nextInt(); // 列数
scanner.nextLine(); // 清除换行符
// 初始化棋盘
char[][] board = new char[rows][cols];
for (int i = 0; i < rows; i++) {
String line = scanner.nextLine().replaceAll(" ", ""); // 去除空格
for (int j = 0; j < cols; j++) {
board[i][j] = line.charAt(j);
}
}
// 对于每一个边界位置,调用 DFS 来标记所有与边界相连的 'O'
// 上下边界
for (int col = 0; col < cols; col++) {
if (board[0][col] == FREE) dfs(board, 0, col); // 第一行
if (board[rows - 1][col] == FREE) dfs(board, rows - 1, col); // 最后一行
}
// 左右边界
for (int row = 1; row < rows - 1; row++) {
if (board[row][0] == FREE) dfs(board, row, 0); // 第一列
if (board[row][cols - 1] == FREE) dfs(board, row, cols - 1); // 最后一列
}
// 更新棋盘:翻转所有未标记的 'O',并将标记恢复为 'O'
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (board[row][col] == FREE) board[row][col] = BLOCKED;
else if (board[row][col] == MARKED) board[row][col] = FREE;
}
}
// 输出修改后的棋盘
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
System.out.print(board[row][col] + (col < cols - 1 ? " " : ""));
}
System.out.println();
}
}
// 深度优先搜索算法,用于标记与边界相连的 'O'
private static void dfs(char[][] board, int row, int col) {
// 如果当前位置超出边界或者不是 'O',则停止递归
if (row < 0 || row >= board.length || col < 0 || col >= board[row].length || board[row][col] != FREE) return;
// 标记当前位置
board[row][col] = MARKED;
// 继续对上下左右四个方向进行递归搜索
dfs(board, row - 1, col); // 上
dfs(board, row + 1, col); // 下
dfs(board, row, col - 1); // 左
dfs(board, row, col + 1); // 右
}
}
全国高校计算机能力挑战赛获奖经验分享

1. 为何参加?赛事含金量/知名度/认可度
全国高校计算机能力挑战赛作为一项国家级赛事,在我国高校计算机教育领域享有较高的知名度和认可度。它不仅是检验学生计算机技能和解决问题能力的重要平台,也是提升个人竞争力、为未来就业或深造增添砝码的好机会。虽然我大一初次参赛未能在省赛中获奖,但这个比赛所蕴含的挑战性和专业性深深吸引了我,激励着我在后续的学习中不断提升自己。
2. 学校政策支持
特别感谢西安石油大学信息化建设与管理处、网络和数据中心的老师同学们,他们不仅提供了必要的技术支持,还创造了良好的学习氛围,极大地促进了我的成长。我校非常重视学生的实践能力和创新能力培养,对于参加此类高水平竞赛的学生给予充分的支持,包括提供考场和设备进行比赛,确保我们能够在最佳状态下参与竞争。此外,学校对本赛事的认可(国家级C级)也表明了其对我们努力的重视和支持。
3. 参赛经验总结和感想
尽管第一次参赛并未取得理想的成绩,但我从中获得了宝贵的经验教训。这次经历教会了我如何更好地准备比赛,理解到了团队合作的重要性,并且认识到了自身知识体系中的不足之处。更重要的是,它让我明白成功并非一蹴而就,而是需要持续不断的努力和积累。在此过程中,我得到了西安石油大学计算机协会成员们的热情帮助和支持,这无疑是我前进道路上的一大动力源泉。
4. 具体参赛技巧和建议
- 真题练习的重要性:往年真题是了解比赛风格和难度的最佳资源。我在赛前刷完了近5年Java赛道的所有真题和模拟题,同时也在LeetCode上做了150道题来进行巩固。通过这种高强度的训练,我能够更加熟悉各种算法和数据结构的应用场景,极大提高了解题速度和准确性。
- 注意题目细节:对题目的输入输出格式一定要多留意。这一点在比赛中尤为重要,因为在决赛最后半小时我发现自己的字符串输出格式有问题,不得不紧急调整。希望以后参赛的大家可以提前检查并测试代码的输入输出格式,避免类似的情况发生。
通过参与全国高校计算机能力挑战赛,我不仅提高了自己的计算机操作技能,更学会了如何面对挫折、保持积极乐观的态度继续前行。这段经历对我来说是非常宝贵的财富,我相信它将对我未来的学术研究和个人发展产生深远的影响。再次向所有支持过我的人表示最诚挚的感谢!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)