CSP认证-202312

目的

        学校要求csp认证,我报的java,是谁三月底就要考了才开始刷题

        直接上手刷真题(吐槽一下csp模拟考试,样例过了但提交错误,也不知道是哪一步错了,也不记录历史解答,并且没找到官方题解真的好难受)

        写博客记录总结一下每个题的思路,方便自己复习,有可以改进的地方也希望佬们指导一下

前言

        官方模拟考试链接:试题清单

        目前只更新了前三题的满分思路,后面两题之有自己的错误解答(求佬指点),先把前三题拿到手再说,随缘更新~

202312

202312-1 仓库规划

满分思路:三层循环。对于仓库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);
        }
    }
}

202312-2 因子化简

(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--;
        }
    }
}

202312-3 树上搜索

满分思路:使用深度遍历计算每个节点的总权重,再计算最小权重差的下标,判断目标节点是否在该下标的子树中,循环以上,直至最后一个节点。

特殊设计:

        ①使用全局变量存储常用数据,注意计算不同输入要清空部分全局变量;

        ②使用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;
    }
}

202312-4 宝藏

解答错误:使用二维数组存储指令序列;根据不同的事件类型,硬编码进行双端队列操作;实现一维数组的矩阵乘法,为了与指令格式匹配,数组第一位不记录,然后出队计算结果;队列为空输出单位矩阵;输出进行取模操作。(官方样例通过,但不知道是哪里导致没通过提交)

注意:矩阵乘法结果可能大于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;
    }
}

202312-5 彩色路径

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、注意题目要求,例如第四题中队列为空则输出单位矩阵,输入结果进行取模操作

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值