CSP认证-202212

前言

        使用java,根据官方模拟考试的试题列表刷题 试题清单

        目前只更新了前三题的满分思路,后面两题先放一放,随缘更新~

202212

202212-1 现值计算

满分思路:计算第k年的x元在今年的价值x/Math.pow(1+i,k),将每年结果累加

注意:题目要将未来的款项转换为今年的价值

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 年数
        double res = 0, i = scanner.nextDouble(); // 年利率
        for (int k = 0; k < n + 1; k++) { // 每年金额
            res += scanner.nextDouble() / Math.pow(1 + i, k);
        }
        System.out.println(res);
    }
}

202212-2 训练计划

(1)70分思路:time[]为每个科目所需时间,dep[i]为每个科目的依赖科目,earliest[]为每个科目的最早开始时间;依次计算earliest[],如果该科目无依赖,则earliest[i]为1,如果有依赖dep[i],则earliest[i]为earliest[dep[i]]+time[dep[i]]

(2)满分思路(优):在70分思路的基础上,latest[]为每个科目的最晚开始时间;在计算earliest[]时,如果earliest[i]+time[i]-1>n,则说明计划不能完成,只需要输出earliest[]即可;如果能完成,先初始化latest[i]为假设无依赖的最晚开始时间n-time[i]+1,再从前往后遍历,由于编号大的科目一定依赖编号小的科目,对于有依赖的科目latest[dep[i]]=min(latest[dep[i]],latest[i]-time[dep[i]),即得latest[i]

import java.util.*;
public class Main {
    static int[] dep; // 依赖关系
    static int[] time; // 需要时间
    static int[] earliest, latest; // 最早开始时间,最晚开始时间

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(); // 天数,科目数
        dep = new int[m + 1];
        time = new int[m + 1];
        earliest = new int[m + 1];
        latest = new int[m + 1];
        boolean isFinish = true; // 是否能完成
        for (int i = 1; i <= m; i++) {
            dep[i] = sc.nextInt(); // 依赖
        }
        for (int i = 1; i <= m; i++) {
            time[i] = sc.nextInt(); // 需要时间
            if (dep[i] == 0) earliest[i] = 1; // 无依赖
            else earliest[i] = earliest[dep[i]] + time[dep[i]]; // 有依赖
            if (earliest[i] + time[i] - 1 > n) isFinish = false;
        }
        // 最早开始时间
        for (int i = 1; i <= m; i++) System.out.print(earliest[i] + " ");
        System.out.println();
        if (isFinish) {
            // 最晚开始时间
            for (int i = m; i > 0; i--) latest[i] = n - time[i] + 1; // 假设都没有依赖
            for (int i = m; i > 0; i--) {
                if (dep[i] != 0 && latest[dep[i]] > latest[i] - time[dep[i]]) // 如果有依赖
                    latest[dep[i]] = latest[i] - time[dep[i]];
            }
            for (int i = 1; i <= m; i++) System.out.print(latest[i] + " ");
            System.out.println();
        }
    }
}

(3)满分思路:对earliest[]的处理与之前一致,对latest[]的处理引入List<Integer>[]next记录每个科目的下一个科目列表,以及last[i]记录该科目后续依赖需要的最大时间;使用递归计算每个科目的之后依赖所需最大时间maxTime,则得到该科目的last[i]=maxTime+time[i],对于每个没有依赖的科目都要进行递归,最终打印latest[i]为n-last[i]+1。下面代码中times[i][0]对应最早开始时间earliest[i],times[i][1]对应每个科目所需时间time[i]

import java.util.*;
public class Main {
    static List<Integer>[] next; // 下一个科目
    static int[] dep; // 依赖关系
    static int[][] times; //最早开始时间、完成时间
    static int[] last; // 后续时间
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(); // 天数,科目数
        next = new List[m+1];
        dep = new int[m + 1];
        times = new int[m + 1][2];
        last = new int[m + 1];
        boolean isFinish = true; // 是否能完成
        for (int i = 1; i <= m; i++) {
            dep[i] = sc.nextInt();
            next[i] = new ArrayList<>();
            if(dep[i]!=0)next[dep[i]].add(i);
        }
        times[0][0] = 1; // 初始化
        for (int i = 1; i <= m; i++) {
            times[i][0] = times[dep[i]][0] + times[dep[i]][1];
            times[i][1] = sc.nextInt();
            if (times[i][0]+times[i][1]-1 > n) isFinish = false;
        }
        // 最早开始时间
        for (int i = 1; i <= m; i++) System.out.print(times[i][0] + " ");
        System.out.println();
        if (isFinish) {
            // 最晚开始时间
            for (int i = 1; i <= m; i++) if(dep[i]==0)getLast(i);
            for (int i = 1; i <= m; i++) System.out.print((n - last[i] + 1) + " ");
            System.out.println();
        }
    }

    public static void getLast(int index){
        int maxTime = 0;
        for(int i : next[index]){
            getLast(i);
            maxTime = Math.max(maxTime,last[i]);
        }
        last[index] = maxTime + times[index][1];
    }
}

202212-3 JPEG解码

        该题的核心主要是矩阵填充和逆余弦变换的计算,计算照着公式一步步来就行,这里思路主要介绍矩阵填充的方法

(1)满分思路:多个if进行边界判断。使用x、y记录下一个要填的下标,填充主要包括斜线和转向两种情况,逐个填充n个数,对于矩阵每个边界进行分析,可得:

a.换向情况:

        ①x==0&&y==0时y+=1

        ②(x==0&&y<7)||(x==7&&y<7)时y+=1

        ③(x<7&&y==0)||(x<7&&y==7)时x+=1

b.斜线情况:

        ①当转向为(x==0&&y<7)||(x<7&&y==7)时,循环x+=1;y-=1,直到边界

        ②当转向为(x==7&&y<7)||(x<7&&y==0)时,循环x-=1;y+=1,直到边界

每次坐标转变后都要进行填充,并判断是否填充了n个

int x = 0, y = 0; // 从左上角开始
imageMatrix[x][y] = sc.nextInt();
for (int k = 1; k < n; ) {
    if (x == 0 && y == 0) y += 1;
    else if ((x == 0 || x == 7) && y < 7) y += 1;
    else if ((y == 0 || y == 7) && x < 7) x += 1;
    imageMatrix[x][y] = sc.nextInt();
    k++;
    if (k == n) break;
    if ((x == 0 && y < 7) || (y == 7 && x < 7)) {
        do {
            x += 1;
            y -= 1;
            imageMatrix[x][y] = sc.nextInt();
            k++;
            if (k == n) isFull = true;
        } while (!isFull && x != 0 && x != 7 && y != 0 && y != 7);
    } else if ((x == 7 && y < 7) || (y == 0 && x < 7)) {
        do {
            x -= 1;
            y += 1;
            imageMatrix[x][y] = sc.nextInt();
            k++;
            if (k == n) isFull = true;
        } while (!isFull && x != 0 && x != 7 && y != 0 && y != 7);
    }
}

(2)满分思路(优):借用下标矩阵。将读入的数据记录在int[64]中,创建一个int[8][8]的矩阵依次存放填充位置对应数据读入的下标,根据i、j找到下标再找到填充数据

int[] scan = new int[64];
int[][] index = {{0, 1, 5, 6, 14, 15, 27, 28},
                {2, 4, 7, 13, 16, 26, 29, 42},
                {3, 8, 12, 17, 25, 30, 41, 43},
                {9, 11, 18, 24, 31, 40, 44, 53},
                {10, 19, 23, 32, 39, 45, 52, 54},
                {20, 22, 33, 38, 46, 51, 55, 60},
                {21, 34, 37, 47, 50, 56, 59, 61},
                {35, 36, 48, 49, 57, 58, 62, 63}};
for (int i = 0; i < n; i++) scan[i] = sc.nextInt();
for (int i = 0; i < 8; i++)
    for (int j = 0; j < 8; j++)
        imageMatrix[i][j] = scan[index[i][j]]; // 图像矩阵

(3)满分思路:根据矩阵下标特性填充。scan[]记录读入的数据;根据矩阵左下右上的斜线性质i+j为常数,会发现i+j为偶数时,从左下到右上,i+j为奇数时,从右上到左下;然后再计算出前面有多少个数,就可以确定填充对应的下标。

a.左下到右上,即i+j为偶数:

        ①上半部分:前面的斜线(1+...+(i+j)等差) + 所在斜线前面个数(j) =  (1+i+j)*(i+j)/2+j

        ②下半部分:上半所有(1+...+7等差=36) + 前面斜线(7+...+(16-i-j)等差) + 所在斜线前面个数(7-i) = 36+(23-i-j)*(i+j-8)/2+7-i

b.右上到左下,即为奇数:

        ①上半部分:前面的斜线(同上) + 所在斜线前面个数(i) =  (1+i+j)*(i+j)/2+i

        ②下半部分:上半所有(同上) + 前面斜线(同上) + 所在斜线前面个数(7-j) = 36+(23-i-j)*(i+j-8)/2+7-j

详细分析+图解见文章:第28次csp认证T3 JPEG 解码解析_cspjt3数据-CSDN博客

int[] scan = new int[64];
for (int i = 0; i < n; i++) scan[i] = sc.nextInt();
for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
        if ((i + j) % 2 == 0) {//偶数,左下到右上
            if (i + j <= 7) { // 上半
                // 1+...+(i+j) + j
                imageMatrix[i][j] = scan[(1 + i + j) * (i + j) / 2 + j];
            } else {// 下半
                // 上半 (1+8)*8/2=36
                // 下半 7+...+(16-i-j) + 7-i
                imageMatrix[i][j] = scan[36 + (23 - i - j) * (i + j - 8) / 2 + 7 - i];
            }
        } else {//奇数,右上到左下
            if (i + j <= 7) { // 上半
                // 1+...+(i+j) + i
                imageMatrix[i][j] = scan[(1 + i + j) * (i + j) / 2 + i];
            } else { // 下半
                // 上半36
                // 下半 7+...+(16-i-j) + 7-j
                imageMatrix[i][j] = scan[36 + (23 - i - j) * (i + j - 8) / 2 + 7 - j];
            }
        }
    }
}

注意:

        ①离散余弦变换中double运算的每个数值都要为double,包括写的常数

        ②结果使用Math.round()进行四舍五入,(为啥我使用(int)(res+0.5)进行四舍五入但是提交不对,蹲个大佬解答一下)

        ③结果大于255取255,小于0取0

剩余部分三种思路都是一样的,这里给一下第二个满分思路的完整代码

import java.util.*;
public class Main {
    static int[][] quantifyMatrix = new int[8][8]; // 量化矩阵
    static int[][] imageMatrix = new int[8][8]; // 图像矩阵
    static double[][] transMatrix = new double[8][8]; // 变换矩阵
    static int[][] decodeMatrix = new int[8][8]; // 解码矩阵

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
                quantifyMatrix[i][j] = sc.nextInt(); // 量化矩阵
        int n = sc.nextInt();
        int T = sc.nextInt();
        int[] scan = new int[64];
        int[][] index = {{0, 1, 5, 6, 14, 15, 27, 28},
                {2, 4, 7, 13, 16, 26, 29, 42},
                {3, 8, 12, 17, 25, 30, 41, 43},
                {9, 11, 18, 24, 31, 40, 44, 53},
                {10, 19, 23, 32, 39, 45, 52, 54},
                {20, 22, 33, 38, 46, 51, 55, 60},
                {21, 34, 37, 47, 50, 56, 59, 61},
                {35, 36, 48, 49, 57, 58, 62, 63}};
        for (int i = 0; i < n; i++) scan[i] = sc.nextInt();
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
                imageMatrix[i][j] = scan[index[i][j]]; // 图像矩阵
        if (T == 0) {
            for (int i = 0; i < 8; i++) {
                for (int j = 0; j < 8; j++)
                    System.out.print(imageMatrix[i][j] + " ");
                System.out.println();
            }
            return;
        }
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
                imageMatrix[i][j] *= quantifyMatrix[i][j]; // 量化后
        if (T == 1) {
            for (int i = 0; i < 8; i++) {
                for (int j = 0; j < 8; j++)
                    System.out.print(imageMatrix[i][j] + " ");
                System.out.println();
            }
            return;
        }
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                for (int u = 0; u < 8; u++) {
                    double au = u == 0 ? Math.sqrt(0.5) : 1.0;
                    double theta1 = (Math.PI / 8.0) * (i + 0.5) * u;
                    for (int v = 0; v < 8; v++) {
                        double av = v == 0 ? Math.sqrt(0.5) : 1.0;
                        double theta2 = (Math.PI / 8.0) * (j + 0.5) * v;
                        transMatrix[i][j] += au * av * imageMatrix[u][v] * Math.cos(theta1) * Math.cos(theta2);
                    }
                }
                decodeMatrix[i][j] = (int) Math.round(transMatrix[i][j] / 4.0 + 128);
                if (decodeMatrix[i][j] > 255) decodeMatrix[i][j] = 255;
                if (decodeMatrix[i][j] < 0) decodeMatrix[i][j] = 0;
                System.out.print(decodeMatrix[i][j] + " ");
            }
            System.out.println();
        }
    }
}

202212总结

        1、第一题指数运算求和

        2、第二题写出最早开始时间保底70分,最晚开始时间需要构思一下,使用latest[dep[i]]=min(latest[dep[i]],laest[i]-time[dep[i]])挺有创意的,也可以使用递归逐个更新

        3、第三题对于数组填充,使用填充下标矩阵真的很巧妙,利用矩阵下标i+j或者i-j的特性也很有用

        4、仔细看清题目的要求。如第一题是将未来价值转换到当前价值;第二题不能完成计划就只输出一行;第三题结果要四舍五入并在[0,255]范围内

        5、存个疑:使用(int)(res+0.5)不能成功四舍五入吗(求大佬解答一下)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值