目的
学校要求csp认证,我报的java,是谁三月底就要考了才开始刷题
直接上手刷真题(吐槽一下csp模拟考试,样例过了但提交错误,也不知道是哪一步错了,也不记录历史解答,并且没找到官方题解真的好难受)
写博客记录总结一下每个题的思路,方便自己复习,有可以改进的地方也希望佬们指导一下
前言
官方模拟考试链接:试题清单
目前只更新了前三题的满分思路,后面两题之有自己的错误解答(求佬指点),先把前三题拿到手再说,随缘更新~
202312
满分思路:三层循环。对于仓库i,比较其余每个仓库j,判断每个位置k的编码codes[i][k]>=codes[j][k],成立则舍弃j,记录剩余j中的最小值
注意:输出的不是下标
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt(), count = scanner.nextInt();
int[][] codes = new int[num][count];
for (int i = 0; i < num; i++) {
for (int j = 0; j < count; j++) {
codes[i][j] = scanner.nextInt();
}
}
for (int i = 0; i < num; i++) {
int target = num;
for (int j = 0; j < num; j++) {
boolean flag = false;
for (int k = 0; k < count; k++) {
if (codes[i][k] >= codes[j][k]) {
flag = true;
break;
}
}
if (!flag) target = Math.min(target, j);
}
if (target == num) target = -1;
System.out.println(target + 1);
}
}
}
(1)80分思路:提前计算出10万内的所有质数列表;对于每个输入,逐个判断质数列表,个数小于k的就被除掉。(循环太多超时,也可能没必要计算10万内的质数)
(2)满分思路:对于每个输入,从2开始判断,能除就直接除,个数大于k的就记录在结果数中,原理是包含的合数会在出现之前就被质数除掉,因此不用单独判断质数;优化为在[2,n/2]的范围内判断
注意:每个数n<=10^10,java中int为32为,数量级为10^9,long为64为,数量级为10^18,使用long存储
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long num = scanner.nextLong();
while (num > 0) {
long res = 1;
long n = scanner.nextLong(), k = scanner.nextLong();
for (int i = 2; i <= n / i; i++) {
long t = 0;
while (n % i == 0) {
n /= i;
t++;
}
if (t >= k)
res *= (long) Math.pow(i, t);
}
System.out.println(res);
num--;
}
}
}
满分思路:使用深度遍历计算每个节点的总权重,再计算最小权重差的下标,判断目标节点是否在该下标的子树中,循环以上,直至最后一个节点。
特殊设计:
①使用全局变量存储常用数据,注意计算不同输入要清空部分全局变量;
②使用List<>[]存储每个节点的孩子,方便访问且孩子便于添加;
③使用两个Set<>分别记录保留节点和舍弃节点,计算总权重时不考虑舍弃节点同时记录保留节点,获取最小下标时只遍历保留节点,判断是否在子树时不考虑舍弃节点,根据判断结果记录舍弃节点;
④记录当前根节点的下标,用于深度遍历的起始节点
注意:节点数n<=2000,每个权重w<=10^7,总权重<=10^10,使用long存储总权重
import java.util.*;
public class Main {
static long[] w, sumW; // 各权重、总权重
static List<Integer>[] children; // 孩子
static Set<Integer> keepNode = new HashSet<>(); // 保留节点
static Set<Integer> removeNode = new HashSet<>(); // 移除节点
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), m = scanner.nextInt();
w = new long[n + 1]; // 权重
sumW = new long[n + 1]; // 总权重
children = new List[n + 1]; // 孩子
for (int i = 1; i <= n; i++) {
w[i] = scanner.nextInt();
children[i] = new ArrayList<>();
}
for (int i = 2; i <= n; i++) {
children[scanner.nextInt()].add(i); // 添加孩子
}
for (int i = 0; i < m; i++) {
int target = scanner.nextInt();
int root = 1;
removeNode.clear();
while (true) {
sumW = Arrays.copyOf(w, n + 1);
keepNode.clear();
getSumW(root); // 计算总权重
if (keepNode.size() == 1) break;
int minIndex = getMinIndex(root); // 权重最小
if (check(target, minIndex)) root = minIndex;
else removeNode.add(minIndex);
System.out.print(minIndex + " ");
}
System.out.println();
}
}
public static void getSumW(int root) {
keepNode.add(root);
for (int child : children[root]) { // 根节点每个孩子
if (!removeNode.contains(child)) {
getSumW(child);
sumW[root] += sumW[child];
}
}
}
public static int getMinIndex(int root) {
long min = Long.MAX_VALUE;
int index = 1;
for (int node : keepNode) {
long temp = Math.abs(sumW[root] - 2 * sumW[node]);
if (temp < min) {
min = temp;
index = node;
}
}
return index;
}
public static boolean check(int target, int minIndex) {
if (minIndex == target) return true;
for (int child : children[minIndex]) {
if (removeNode.contains(child)) continue;
if (check(target, child)) return true;
}
return false;
}
}
解答错误:使用二维数组存储指令序列;根据不同的事件类型,硬编码进行双端队列操作;实现一维数组的矩阵乘法,为了与指令格式匹配,数组第一位不记录,然后出队计算结果;队列为空输出单位矩阵;输出进行取模操作。(官方样例通过,但不知道是哪里导致没通过提交)
注意:矩阵乘法结果可能大于int,使用long存储
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), m = scanner.nextInt();
long[][] instructs = new long[n][5]; // 读入指令
for (int i = 0; i < n; i++) {
instructs[i][0] = scanner.nextInt();
if (instructs[i][0] != 3)
for (int j = 1; j < 5; j++) instructs[i][j] = scanner.nextLong();
}
for (int i = 0; i < m; i++) { // 每个事件
int type = scanner.nextInt();
if (type == 1) {
int t = scanner.nextInt() - 1;
instructs[t][0] = scanner.nextInt();
if (instructs[t][0] != 3)
for (int j = 1; j < 5; j++) instructs[t][j] = scanner.nextLong();
} else if (type == 2) {
Deque<long[]> queue = new ArrayDeque<>();
int l = scanner.nextInt(), r = scanner.nextInt(), last = 0; // 0为头部,1为尾部
for (int t = l - 1; t < r; t++) {
long instructType = instructs[t][0];
if (instructType == 1) {
queue.offerFirst(instructs[t]);
last = 0;
} else if (instructType == 2) {
queue.offerLast(instructs[t]);
last = 1;
} else if (instructType == 3) {
if (!queue.isEmpty()) {
if (last == 0) queue.pollFirst();
else queue.pollLast();
}
}
}
if (!queue.isEmpty()) {
long[] res = queue.pollFirst();
while (!queue.isEmpty())
res = matrixPlus(res, queue.pollFirst());
for (int t = 1; t <= 4; t++) System.out.print(res[t] % 998244353 + " ");
} else System.out.print("1 1 1 1");
System.out.println();
}
}
}
public static long[] matrixPlus(long[] m1, long[] m2) {
long[] res = new long[5];
res[1] = m1[1] * m2[1] + m1[2] * m2[3];
res[2] = m1[1] * m2[2] + m1[2] * m2[4];
res[3] = m1[3] * m2[1] + m1[4] * m2[3];
res[4] = m1[3] * m2[2] + m1[4] * m2[4];
return res;
}
}
2分运行超时:使用List<int[]>[]记录每个起点对应的不同终点和长度,Set<>记录当前路径包含的颜色;使用递归进行深度优先遍历,根据节点数和颜色剪枝,颜色不重复包含节点不重复;记录最长路径的长度。(通过样例但提交运行超时)
import java.util.*;
public class Main {
static int maxLength = 0; // 最长路径长度
static int[] Color; // 颜色标签
static int[] Start, End, Length; // 起点、终点、长度
static List<int[]>[] roads; // 所有路径信息
static Set<Integer> tempColor = new HashSet<>(); // 当前所有颜色
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), m = scanner.nextInt(), l = scanner.nextInt(), k = scanner.nextInt(); //节点数、边数、上限、颜色范围
Color = new int[n];
Start = new int[m];
End = new int[m];
Length = new int[m];
for (int i = 0; i < n; i++) Color[i] = scanner.nextInt();
for (int i = 0; i < m; i++) Start[i] = scanner.nextInt();
for (int i = 0; i < m; i++) End[i] = scanner.nextInt();
for (int i = 0; i < m; i++) Length[i] = scanner.nextInt();
roads = new List[n];
for (int i = 0; i < n; i++) {
roads[i] = new ArrayList<>();
}
for (int i = 0; i < m; i++) {
roads[Start[i]].add(new int[]{End[i], Length[i]});
}
// 深度优先
dfs(new int[]{0, 0}, n - 1, l, 0);
System.out.println(maxLength);
}
public static void dfs(int[] start, int target, int l, int sumLength) {
if (l == 0 || tempColor.contains(Color[start[0]])) return;
if (start[0] == target) {
maxLength = Math.max(maxLength, sumLength + start[1]);
return;
}
tempColor.add(Color[start[0]]);
for (int[] next : roads[start[0]]) {
dfs(next, target, l - 1, sumLength + start[1]);
}
tempColor.remove(Color[start[0]]);
}
}
202312总结
1、第一题暴力求解,第二题质数先于合数被除,第三题树结构+深度优先遍历
2、注意数据大小,决定是否使用long
3、注意下标和编号的区别
4、注意题目要求,例如第四题中队列为空则输出单位矩阵,输入结果进行取模操作