单选题
1.存储稀疏矩阵时,以下哪种方式对内存较优(D)
A.散列
B.最小堆
C.二维数组
D.三元组
2.对n个基本有序的整数进行排序,采用插入排序算法和快速排序算法的时间复杂度分别是:(D)
A.O(n^2)和O(nlogn)
B.O(n2)和O(n2)
C.O(n)和O(nlogn)
D.O(n)和O(n^2)
3.在一个抽卡系统中,抽到稀有卡的概率是1/4,且如果连续两次没抽到稀有卡,第三次必定抽到稀有卡,那么抽到稀有卡的实际概率是(B)
A.9/37
B.16/37
C.23/37
D.30/37
解析:
状态定义
- 状态0:当前抽卡前未连续失败(如首次抽卡或最近一次抽卡成功)
- 状态1:当前抽卡前最近一次失败
- 状态2:当前抽卡前已连续两次失败(触发保底)
状态转移规则
从状态0:
- 抽中成功(概率1/4)→ 回到状态0
- 抽中失败(概率3/4)→ 进入状态1
从状态1:
- 抽中成功(概率1/4)→ 回到状态0
- 抽中失败(概率3/4)→ 进入状态2
从状态2:
- 必中成功(概率100%)→ 回到状态0
列出稳态概率方程
- π₀ = (1/4)π₀ + (1/4)π₁ + 1·π₂
- π₁ = (3/4)π₀
- π₂ = (3/4)π₁
- π₀ + π₁ + π₂ = 1
解方程得稳态分布
- π₀ = 16/37(状态0概率)
- π₁ = 12/37(状态1概率)
- π₂ = 9/37(状态2概率)
抽到稀有卡的概率:
= 1/4 * 16/37 + 1/4 * 12/37 + 1 * 9/37
= 4/37 + 3/37 + 9/37 = 16/37
4. 在32位平台下,以下这个MyUnion的sizeof大小是多少?(A)
union MyUnion{
struct {
char a;
double b;
}s1;
struct{
int c;
float d;
}s2;
};
A.16
B.24
C.8
D.12
5.在轮转调度算法中,时间片长度过短会导致什么问题?(C)
A.任务饥饿
B.平均等待时间增加
C.系统开销增加
D.任务响应时间变长
6.关于C++中的多态,以下哪种描述是正确的?(D)
A. override关键字可以用于声明多态函数
B.多态不能通过基类指针或引用调用派生类覆盖的虚函数
C. super关键字可以显式地调用基类的成员函数
D.如果一个类包含至少一个纯虚函数,则不能直接实例化
解析:
多态函数本身是由 virtual 关键字在基类中声明的。C++ 的多态性正是通过基类指针或引用调用派生类覆盖的虚函数实现的,这是运行时多态的核心机制。在 C++ 中,没有 super 关键字。
7.在TCP拥塞控制中,拥塞避免阶段使用哪种算法来调整拥塞窗口?( C )
A.快速重传算法
B.慢启动算法
C.加性增、乘性减(AIMD)算法
D.快速恢复算法
8.某游戏正在升级一套管理系统。该系统用于在数据库中管理玩家的战力、积分、装备数据。系统经重新整合后,开发人员决定不再使用一张备份数据表scoreinfo表,需永久删除。请选出符合要求的语句:(A)
A. DROP TABLE scoreinfo
B. DROP FROM TABLE scoreinfo
C. DELETE TABLE scoreinfo
D. DELETE FROM TABLE scoreinfo
9.以下哪个不是常见的抗锯齿算法:(B)
A. SMAA子像素增强抗锯齿
B. RTAA光线追踪抗锯齿
C. FXAA快速近似抗锯齿
D. MSAA多重采样抗锯齿
解析:
光线追踪带来的抗锯齿效果是其渲染方法的自然结果,而不是一个独立的算法。
10. 以下哪种技术能最有效减少机器学习模型的过拟合?( C )
A.使用更复杂的模型
B.数据标准化
C.正则化
D.增加训练数据
编程题
1.假设有一类数字num,可以表示为num = a8*(10^7) + a7*(10^6) + …+ a2*(10^1) + a1。其中a1~a8为不大于7的非负整数。请设计一个算法,计算这一类数字中,一共有多少个奇数?
题解
public class Main {
public static void main(String[] args){
int num_a1 = 4;
int num_a2_a8 = (int)Math.pow(8,7);
long ans = num_a1*num_a2_a8;
System.out.println(ans);
}
}
2.在一个二维网格中,有一个起点S、一个终点E和一个必须经过的中间点M。网格中的障碍物用#表示,空地用.表示。每次可以向上、下、左、右移动一格。请实现一个算法,找到从起点到终点且经过中间点的最短路径长度。如果无法到达终点,则返回-1。
示例
输入:
4 4
S..M
##.#
….
.##E
输出:
8
题解:
import java.util.*;
class Point{
int x;
int y;
public Point(int x,int y){
this.x = x;
this.y = y;
}
}
public class Main {
private static final int[][] direction = {{0,1},{0,-1},{1,0},{-1,0}};
private static int row, col;
public static int bfs(char[][] grid, Point start,Point end){
boolean[][] visited = new boolean[row][col];
Queue<Point> queue = new LinkedList<>();
queue.add(start);
visited[start.x][start.y]=true;
int steps = 0;
while(!queue.isEmpty()){
int size = queue.size();
for(int i=0;i<size;i++){
Point cur = queue.poll();
if(cur.x==end.x&&cur.y==end.y){
return steps;
}
for(int[] dir:direction){
int nextX = cur.x+dir[0];
int nextY = cur.y+dir[1];
if(nextX>=0 && nextX<row&&nextY>=0&&nextY<col&&grid[nextX][nextY]!='#'&&!visited[nextX][nextY]){
queue.add(new Point(nextX,nextY));
visited[nextX][nextY]=true;
}
}
}
steps++;
}
return -1;
}
public static void main(String[] args){
Scanner in = new Scanner(System.in);
row = in.nextInt();
col = in.nextInt();
in.nextLine();
char[][] grid = new char[row][col];
Point start = null,middle = null,end = null;
for(int i=0;i<row;i++){
String line = in.nextLine();
for(int j=0;j<col;j++){
grid[i][j] = line.charAt(j);
if(grid[i][j]=='S'){
start = new Point(i,j);
}else if(grid[i][j]=='M'){
middle = new Point(i,j);
}else if(grid[i][j]=='E'){
end = new Point(i,j);
}
}
}
int ans1 =bfs(grid,start,middle);
if(ans1 == -1){
System.out.println(-1);
return;
}
int ans2 =bfs(grid,middle,end);
if(ans2 == -1){
System.out.println(-1);
return;
}
System.out.println(ans1+ans2);
}
}
3.有一排宝石(共M个),每个宝石都有一个能量值。你可以选择任意连续K个宝石合并成一个新的宝石,新宝石保持在原队列的位置且它的能量值是这K个宝石能量值之和。其中有一个幸运值,合并后的能量会直接添加到幸运值上;给定一个幸运值N=0、一堆包含能量值的宝石,你的任务是找到将所有宝石合并成一个宝石的最大幸运值。
要求:
2<=K<=M;
【示例一】
输入:
可连续合并的宝石数K:K=4
宝石具体能量值:[10, 7, 2, 6, 5, 11, 8];
输出:
79
【示例二】
输入:
可连续合并的宝石数K:K=3
宝石具体能量值:[10, 7, 2, 6, 5, 11];
输出:
-1
解释:合并到最后剩余宝石数量=2<K,因此合成失败返回-1。
【示例三】
输入:
可连续合并的宝石数K:K=2
宝石具体能量值:[6,5,4,1,0,5,7];
输出:
107
题解
public class Main{
public static int solve(int K, int[] gems) {
int M = gems.length;
if (M < K) {
return -1;
}
long[][] dp = new long[M][M]; // dp[i][j]: 合并 gems[i...j] 的最大幸运值
long[] prefixSum = new long[M + 1]; // 前缀和,用于快速计算区间和
// 计算前缀和
for (int i = 0; i < M; i++) {
prefixSum[i + 1] = prefixSum[i] + gems[i];
}
// 区间长度从小到大进行迭代
for (int len = K; len <= M; len++) {
for (int i = 0; i <= M - len; i++) {
int j = i + len - 1; // 区间右端点
// 遍历所有可能的分割点
for (int k = i; k < j; k++) {
dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k + 1][j]); // 分为两段
}
//如果当前区间能够合并
if((len - 1) % (K-1) == 0){
dp[i][j] += prefixSum[j + 1] - prefixSum[i]; // 加上合并后的幸运值
}
}
}
if ((M - 1) % (K - 1) != 0) {
return -1;
}
return (int) dp[0][M - 1];
}
}