前言
使用java,根据官方模拟考试的试题列表刷题 试题清单
目前只更新了前三题的思路,并且第三题跟着找到的满分答案但只得了90分(求佬指点),后面两题先放一放,随缘更新~
202305
满分思路:Map记录某个局面对应的出现次数,使用StringBuilder拼接每局局面
java:Map查询key是否存在containsKey(key),StringBuilder清空setLength(0)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.nextLine();
Map<String, Integer> situations = new HashMap<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
for (int t = 0; t < 8; t++) {
sb.append(scanner.nextLine()); // 每一行
}
String temp = sb.toString();
int time = 1;
if (situations.containsKey(temp)) time += situations.get(temp);
situations.put(temp, time);
System.out.println(time);
sb.setLength(0); // 清空StringBuilder
}
}
}
(1)70分思路:暴力求解,按照题目公式的顺序,每次矩阵运算使用三重循环
(2)满分思路:利用矩阵乘法结合率进行优化,原顺序(Q*K)*V,n*n*d,变顺序Q*(K*V),n*d*d
注意:矩阵运算结果使用long存储
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), d = scanner.nextInt(); // 矩阵大小
long[][] Q = new long[n][d], K = new long[d][n], V = new long[n][d];
long[] W = new long[n];
long[][] resKV = new long[d][d], res = new long[n][d];
for(int i = 0; i < n; i++){ // Q
for(int j = 0; j < d; j++){
Q[i][j] = scanner.nextLong();
}
}
for(int i = 0; i < n; i++){ // K
for(int j = 0; j < d; j++){
K[j][i] = scanner.nextLong();
}
}
for(int i = 0; i < n; i++){ // V
for(int j = 0; j < d; j++){
V[i][j] = scanner.nextLong();
}
}
for(int i = 0; i < n; i++){
W[i] = scanner.nextLong();
}
for(int i = 0; i < d; i++){
for(int j = 0; j < d; j++){
for(int k = 0; k < n; k++){ // KV
resKV[i][j] += K[i][k] * V[k][j];
}
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
for(int k = 0; k < d; k++){ // QKV
res[i][j] += Q[i][k] * resKV[k][j];
}
res[i][j] *= W[i]; // W
System.out.print(res[i][j]+" ");
}
System.out.println();
}
}
}
90分思路:内存超限。只找到了c++的满分版本,就按照思路改成了java版,但是内存超出,不知道问题出在哪里(求佬指点)。使用char[]存储所有被压缩数据,index记录此时处理字符的下标处,逐个判断各种情况;getTwo(char c1,char c2)将两个char表示的一个byte转换为二进制,getSmall(int num)将后面num个小端序的十六进制转为十进制。
java:
①二/十六进制String转十进制:Integer.parseInt(String,2/16)
②十六进制char转十进制:Character.digit(char,16)
③十进制转二进制String:Integer.toBinaryString(int)
二进制结果始终为四位:String.format("%4s",String).replace(' ','0')
import java.util.*;
public class Main {
static char[] data; // 被压缩数据
static int index = 0; // 下标
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int s = scanner.nextInt(); // 被压缩数据字节数
scanner.nextLine(); // 清空换行符
StringBuilder res = new StringBuilder(); // 结果
// 数据
StringBuilder dataString = new StringBuilder();
for (int i = 0; i < (s + 7) / 8; i++) dataString.append(scanner.nextLine());
if (dataString.length() != 2 * s) return; // 长度不一致
data = dataString.toString().toCharArray();
// 引导域
while (index < 8) { // 引导域长度不超过四个字节
if (getTwo(data[index++], data[index++]).charAt(0) != '1') break; // 首位不为1
}
// 数据域
while (index < s*2) {
String twoString = getTwo(data[index++], data[index++]); // 获取后两个字符的二进制
char six = twoString.charAt(6), seven = twoString.charAt(7);
if (six == '0' && seven == '0') { // 字面量
int l = Integer.parseInt(twoString.substring(0, 6), 2) + 1; // 随后l个字节
if (l > 60) l = getSmall(l - 60) + 1; // 存储长度字节小端序
while (l-- > 0)
res.append(data[index++]).append(data[index++]);
} else if (six == '1' && seven == '1') return; // 不合法
else { // 回溯引用
int o = 0, l = 0; // 偏移量、长度
if (six == '0' && seven == '1') { // 回溯引用1
o = Integer.parseInt(twoString.substring(0, 3) + getTwo(data[index++], data[index++]), 2); // 偏移量
l = Integer.parseInt(twoString.substring(3, 6), 2) + 4; // 长度
} else if (six == '1' && seven == '0') { // 回溯引用2
o = getSmall(2); // 后两个字节的小端序
l = Integer.parseInt(twoString.substring(0, 6), 2) + 1; // 长度
}
int size = res.length();
while (l > o) {
res.append(res.substring(size - 2 * o, size));
l -= o;
}
res.append(res.substring(size - 2 * o, size - 2 * o + 2 * l));
}
}
int size = res.length(), start , end = 0;
while (end < size) { // 输出
start = end;
end = Math.min(end + 16, size);
System.out.println(res.substring(start, end));
}
}
public static String getTwo(char c1, char c2) { // 十六进制转二进制
int a = Character.digit(c1, 16), b = Character.digit(c2, 16);
String s1 = String.format("%4s", Integer.toBinaryString(a)).replace(' ', '0'); // 补零
String s2 = String.format("%4s", Integer.toBinaryString(b)).replace(' ', '0');
return s1 + s2;
}
public static int getSmall(int num) { // 后num个字节小端序十六进制转二进制
StringBuilder res = new StringBuilder();
for (int k = 0; k < num; k++) {
res.insert(0, data[index + 1]).insert(0, data[index]);
index += 2;
}
return Integer.parseInt(res.toString(), 16);
}
}
202305总结
1、第一题字符比较转换为字符串比较,计数使用Map
2、第二题矩阵乘法的时间复杂度优化
3、第三题各进制转换