前言
使用java,根据官方模拟考试的试题列表刷题 试题清单
目前只更新了前三题的满分思路,后面两题先放一放,随缘更新~
202212
满分思路:计算第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);
}
}
(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];
}
}
该题的核心主要是矩阵填充和逆余弦变换的计算,计算照着公式一步步来就行,这里思路主要介绍矩阵填充的方法
(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)不能成功四舍五入吗(求大佬解答一下)