⚠️ 注:本文信息借助不可信回忆+AI生成,不构成任何形式的指南或保证。本文信息可能过时或不适用于你的具体情况。因参考本文产生的任何后果,作者概不负责。
时间:2025年9月12日-9月13日
形式和考察范围等和夏令营基本一致
考察了:
-
命题逻辑、一阶逻辑
-
已知p1, p2, p3 = softmax(z1, z2, z3),真实标签为y1, y2, y3,求 softmax 关于输入求导,并结合交叉熵损失
设:
-
模型输出(logits)为:
z=[z1,z2,z3]
-
经过 Softmax 后的预测概率为:
pi=softmax(zi)=∑k=13ezkezi=Sezi,S=k=1∑3ezk
-
真实标签为 one-hot 编码形式:
y=[y1,y2,y3],其中 yi∈{0,1},∑yi=1
-
交叉熵损失函数为:
L=−i=1∑3yilogpi
我们的目标是求:损失 L 对 logits zj 的导数 ∂zj∂L,这在反向传播中非常重要。
先计算 ∂zj∂pi。
情况 1:当 i=j
∂zi∂pi=∂zi∂(Sezi)=S2ezi⋅S−ezi⋅ezi=Sezi−S2e2zi=pi−pi2=pi(1−pi)
情况 2:当 i=j
∂zj∂pi=∂zj∂(Sezi)=ezi⋅(−S2ezj)=−S2eziezj=−pipj
所以统一写成:
∂zj∂pi={pi(1−pi),−pipj,i=ji=j=pi(δij−pj)
其中 δij 是 Kronecker delta 函数(i=j 时为 1,否则为 0)。
∂zj∂L=∂zj∂(−i=1∑3yilogpi)=−i=1∑3yi⋅pi1⋅∂zj∂pi
代入上面的导数表达式:
∂zj∂L=−i=1∑3yi⋅pi1⋅pi(δij−pj)=−i=1∑3yi(δij−pj)
拆开求和:
=−(i=1∑3yiδij−i=1∑3yipj)=−(yj−pji=1∑3yi)
由于 ∑yi=1(one-hot 标签),所以:
∂zj∂L=−(yj−pj)=pj−yj
总结
令:
- p=softmax(z)
- y 为 one-hot 真实标签
则:
∇zL=p−y
这个结果意味着,在实现分类网络时,Softmax + CrossEntropyLoss 的反向传播梯度就是预测概率与真实标签之差。
此外,现代框架(如 PyTorch)提供 LogSoftmax
和 NLLLoss
或直接 CrossEntropyLoss
,内部已融合优化,避免显式计算 log(pi) 带来的数值问题。
-
动态规划:nxm 矩阵,左下角到右上角,每一步可以→↑↗,求方法总数
-
判断完全二叉树
-
关系分类(关系是不是反射的?)、判断映射(满射?)
-
贝叶斯定理
-
线性回归 wx+b
-
CNN 参数量计算
前两题为强制使用python的算法题,后两题为强制使用C++的算法题。
Q2 最长“山形”子序列#
题目描述#
给定一个长度为 n 的整数数组 a,求最长的子序列长度,该子序列满足:先严格递增,后严格递减。
特别地,以下情况也被视为合法的“山形”子序列:
- 仅包含严格递增部分(不下降部分)
- 仅包含严格递减部分(不上升部分)
- 仅包含一个元素
子序列不要求连续。
输入格式
第一行一个正整数 n,表示数组长度。
第二行 n 个整数 a1,a2,…,an,表示数组元素。
输出格式
输出一个整数,表示满足条件的最长子序列的长度。
样例
样例输入 1
样例输出 1
样例解释 1
一个最长的合法子序列为:1 3 5 7 4
或 1 3 5 6 4
,长度为 5。
样例输入 2
样例输出 2
样例解释 2
整个序列严格递减,是合法的(只有下降部分)。
样例输入 3
样例输出 3
样例解释 3
整个序列严格递增,是合法的(只有上升部分)。
数据范围
- 对于 30% 的数据,1≤n≤100
- 对于 60% 的数据,1≤n≤1000
- 对于 100% 的数据,1≤n≤105,1≤ai≤109
提示
子序列可以从原序列中不连续地选取元素,但必须保持原有顺序。
解题思路#
我们可以使用动态规划:
- 计算以每个位置
i
结尾的最长严格递增子序列长度 dp_inc[i]
- 计算以每个位置
i
开始的最长严格递减子序列长度 dp_dec[i]
- 对于每个位置
i
,它可以作为“山顶”,则通过它的最长山形子序列长度为:dp_inc[i] + dp_dec[i] - 1
(减1是因为山顶被计算了两次)
- 答案就是所有位置中的最大值
a = list(map(int, input().split()))
# dp_inc[i]: 以位置 i 结尾的最长严格递增子序列长度
dp_inc[i] = max(dp_inc[i], dp_inc[j] + 1)
# dp_dec[i]: 从位置 i 开始的最长严格递减子序列长度
for i in range(n-2, -1, -1):
dp_dec[i] = max(dp_dec[i], dp_dec[j] + 1)
length = dp_inc[i] + dp_dec[i] - 1
max_length = max(max_length, length)
Q3 字符串平移拼接序列的第 K 个字符#
题目描述#
定义一个字符串生成序列 S0,S1,S2,…,规则如下:
- 初始值:S0="a"
- 递推规则:对于 i≥1,有 Si=Si−1+shift(Si−1)
- 其中 shift(T) 表示将字符串 T 中每个字符按字母表向后平移一位:
- ’a’→’b’, ’b’→’c’, …, ’y’→’z’, ’z’→’a’
- 即循环右移:字符 c 变为 (c−’a’+1)mod26+’a’
例如:
- S0="a"
- S1="a" + shift("a")="a" + "b"="ab"
- S2="ab" + shift("ab")="ab" + "bc"="abbc"
- S3="abbc" + shift("abbc")="abbc" + "bccd"="abbcbccd"
- S4="abbcbccd" + shift("abbcbccd")="abbcbccd" + "bccdcdee"="abbcbccdbccdcdee"
给定一个正整数 k,请求出在某个足够大的 n 下,Sn 的第 k 个字符(1-indexed)。
注意:由于 Sn 的长度随 n 指数增长,你不需要构造整个字符串,而应设计高效算法直接定位第 k 个字符。
输入格式
一行一个正整数 k,表示查询位置(从 1 开始计数)。
输出格式
输出一个字符,表示序列中第 k 个位置上的小写字母。
样例
样例输入 1
样例输出 1
样例输入 2
样例输出 2
样例解释 2
S2="abbc",第 3 个字符是 ’b’。
样例输入 3
样例输出 3
样例解释 3
S3="abbcbccd",第 7 个字符是 ’c’。
数据范围
- 对于 30% 的数据,1≤k≤103
- 对于 60% 的数据,1≤k≤106
- 对于 100% 的数据,1≤k≤1018
提示
- 字符串长度满足:len(S0)=1,len(Si)=2×len(Si−1)
- 即 len(Si)=2i
- 可通过递归方式求解:若 k≤2n−1,则答案在左半部分;否则在右半部分(需还原平移操作)。
解题思路#
我们观察到:
- S0="a", 长度为 20=1
- Si=Si−1+shift(Si−1),长度为 2i
关键性质:
- 每次操作将字符串长度翻倍
- 前半部分就是 Si−1
- 后半部分是前半部分整体字符向后平移一位(z→a)
因此我们可以递归求解第 k 个字符:
- 找到最小的 n 使得 2n≥k,即确定 k 所在的字符串层级
- 如果 k==1 且当前视为 S0,返回
'a'
- 若 k≤2n−1,说明在左半部分,递归处理 Sn−1
- 若 k>2n−1,说明在右半部分,对应位置为 k−2n−1,递归得到该位置在左半部分的原始字符,再将其逆平移(向前移一位)
注意:右半部分比左半部分每个字符大1,所以我们找到对应位置的字符后要还原。
char solve(long long k) {
length <<= 1; // 相当于 length *= 2
long long half = length >> 1; // 左半部分长度 = length / 2
// 在右半部分,对应左半部分的位置是 k - half
char c = solve(k - half);
// 右半部分字符 = 左半部分字符 + 1 (循环)
return 'a' + (c - 'a' + 1) % 26;
cout << solve(k) << endl;
复杂度分析#
- 时间复杂度:O(logk),每次递归将问题规模减半
- 空间复杂度:O(logk),递归栈深度
该算法可以高效处理 k≤1018 的情况。
自我介绍
英文:未来研究计划
项目
为何项目和科研兴趣不一致
机器学习 决策树
对三维重建有什么了解
自己的优点和缺点
transformer