扫描数组找最大值
题意:数组为 [3, 1, 4, 5, 2],ans 从 0 开始;遇到比 ans 大的元素就更新 ans。输出什么?
解题过程
a = [3, 1, 4, 5, 2]
ans = 0
for i in range(5):
if a[i] > ans:
ans = a[i]
print(ans)| 读到的元素 | 更新后的 ans |
|---|---|
| 3 | 3 |
| 1 | 3 |
| 4 | 4 |
| 5 | 5 |
| 2 | 5 |
循环始终保存已经读过的元素中的最大值;最后输出 5。
根据《HH CO 30.pdf》的印刷正文独立解答。覆盖卷一 K 版,以及卷二 O 版 Python 部分;含 31 道题、全部小问和代码填空。
中文解析可离线阅读含推导与代码核验这是独立题解,不是官方答案表。仅以扫描件中的印刷题干、代码和图形连线为依据;铅笔圈选、勾叉、手写数字、草稿及对图形的涂画均不作为已知条件或答案来源。原始 PDF 未作修改。
文件共 23 页:第 1–12 页为卷一封面及 K-1 至 K-11;第 13–23 页为卷二封面及 Python 的 O-1 至 O-8,其中 PDF 第 19、20 页分别重复第 17、18 页。没有卷二 C++ 题页,因此不猜补未提供题目。卷一同时印有 Python/C++ 的共通题,按其共同语义解析。
题干在下文作忠实摘要,代码与关键数据按印刷正文整理。答案栏给出一种有效填法,代码填空可能有等价写法。扫描件没有答案纸,无法核对每个填空的格数上限;下列填法已尽量简短。页码同时列出印刷页码及 PDF 页序,便于回查。
来源日期为试卷印刷的 2026 年 11 月 7 日。网页无外部脚本、字体或网络依赖。
点题号跳转。所有答案均在下方附有推导;代码填空按空格标签列出。
| 题号 | 内容 | 答案 |
|---|---|---|
| 卷一 甲1 | 扫描数组找最大值 | C · 5 |
| 卷一 甲2 | 队列与栈的操作顺序 | B · 3 |
| 卷一 甲3 | 字符串的字典序 | D · 只有 i |
| 卷一 甲4 | 必要条件与逆否命题 | D · 只有 ii |
| 卷一 甲5 | 逐行求和应在哪里清零 | D · 在第 4 与第 5 行之间清零 |
| 卷一 甲6 | 最少区间覆盖 | A · 3 次 |
| 卷一 甲7 | 稳定排序与主次关键字 | C |
| 卷一 甲8 | 三台工作站的最优配对 | B · 12 分钟 |
| 卷一 甲9 | 由相邻最大值还原排列 | B · 12 种 |
| 卷一 甲10 | 翻转操作的奇偶不变量 | D · 50 个 N |
| 题号 | 内容 | 答案 |
|---|---|---|
| 卷一 乙1 | 复合赋值与取余 | A = 2026 |
| 卷一 乙2 | 下标为奇数或值为奇数 | B = 11 |
| 卷一 乙3 | 五天八根香蕉的限制计数 | C = 7 |
| 卷一 乙4 | 循环终止区间与边界 | D = 125 |
| 卷一 乙5 | 从新最大值和新最小值反推排列 | E = 4,2,3,5,6,1 |
| 卷一 乙6 | 四种运算的最大结果 | F = 30 |
| 题号 | 内容 | 答案 |
|---|---|---|
| 卷二 Python 甲1 | 字符串拼接与整数相加 | B · 1234,46 |
| 卷二 Python 甲2 | 依次赋值交换变量 | C · 8 5 |
| 卷二 Python 甲3 | 带条件的列表推导式 | A · 30 |
| 卷二 Python 甲4 | 取模实现循环下标 | D · 21 |
| 卷二 Python 甲5 | 二维列表重复产生的共享引用 | C · 7 |
| 题号 | 内容 | 答案 |
|---|---|---|
| 卷二 Python 乙1 | 双重取余的周期 | A = 24 |
| 卷二 Python 乙2 | 原地更新与反向下标 | B = 5138428 |
| 卷二 Python 乙3 | 异或消去与十轮周期 | C = 23 |
| 卷二 Python 乙4 | 字母 I、O 转为整数 | D = x==I |
| 卷二 Python 乙5 | 按给定输入输出表补条件 | E = s[:2]!="ca" |
| 卷二 Python 乙6 | 用成员关系异或求对称差 | F = (x in s)!=(x in t) |
| 题号 | 内容 | 答案 |
|---|---|---|
| 卷二 Python 丙1 | 判断非零整数是否全部同号 | G = pos==n or neg==n;H = a[0];I1 = a[i];I2 = val*val |
| 卷二 Python 丙2 | 移动一个元素到末尾 | J = a[i+1];K1 = a[i+1]!=b[i];K2 = a[p]==b[n-1] |
题意:数组为 [3, 1, 4, 5, 2],ans 从 0 开始;遇到比 ans 大的元素就更新 ans。输出什么?
a = [3, 1, 4, 5, 2]
ans = 0
for i in range(5):
if a[i] > ans:
ans = a[i]
print(ans)| 读到的元素 | 更新后的 ans |
|---|---|
| 3 | 3 |
| 1 | 3 |
| 4 | 4 |
| 5 | 5 |
| 2 | 5 |
循环始终保存已经读过的元素中的最大值;最后输出 5。
题意:Q 是队列,S 是栈,最初均为空。依题目顺序入队、入栈,并在两者之间转移元素,求最后弹出 Q 的元素。
队列先进先出,栈后进先出。下表的队列左端为队首,栈右端为栈顶。
| 操作 | Q(队首 → 队尾) | S(栈底 → 栈顶) |
|---|---|---|
| 1 入 Q | [1] | [] |
| 2 入 S | [1] | [2] |
| 3 入 Q | [1, 3] | [2] |
| 4 入 S | [1, 3] | [2, 4] |
| S 弹出 4,放入 Q | [1, 3, 4] | [2] |
| Q 弹出 1,放入 S | [3, 4] | [2, 1] |
| Q 再弹出并输出 | [4] | [2, 1] |
最后弹出的是 3。
题意:函数 f(x, y) 返回字符串比较 x < y。判断 i: f("0.11", "0.9");ii: f("six", "seven");iii: f("30", "100")。
| 比较 | 第一个不同的字符 | 结果 |
|---|---|---|
| "0.11" < "0.9" | '1' < '9' | True |
| "six" < "seven" | 'i' > 'e' | False |
| "30" < "100" | '3' > '1' | False |
字符串逐字符比较,不能把它们当作数字大小或英文数词的数值比较。
题意:Alice 只有在看见北极星时才会观星;多云则看不见北极星。已知今晚看得见北极星。i「Alice 今晚观星」与 ii「今晚非多云」,哪些必然成立?
设 A 表示外出观星,P 表示看见北极星,C 表示多云。题设是 A ⇒ P 和 C ⇒ ¬P。
P ⇒ ¬C。今晚 P 为真,故今晚非多云,ii 成立。不能把 A ⇒ P 直接反过来用成 P ⇒ A。
题意:二维数组为 [[2, 4, 1], [5, 0, 2]]。原程序把 row_sum 初始化在外层循环之前。怎样修改才能输出 7 7?
a = [[2, 4, 1], [5, 0, 2]]
row_sum = 0
for row in range(2):
row_sum = 0 # 选项 D:进入每一行时清零
for col in range(3):
row_sum += a[row][col]
print(row_sum, end=" ")两行的和分别是 2+4+1=7、5+0+2=7。原程序没有在换行时清零,会输出 7 14。
选项 A 把累加改成赋值,最后只留下每行末项 1、2;B 只改变输出的除法,得到 2、4;C 在内层每一列都清零,也只留下末项。因此应选 D。
C++ 对应插入 row_sum = 0;,位置也是外层循环内、内层循环前。
题意:检查点为 1, 2, 5, 6, 7, 10, 11, 15。一次扫描可覆盖整数起点 x 到 x+4(两端包含),求最少扫描次数。
三次可行。又因为 1、6、11 两两相差至少 5,而同一扫描内两点的距离至多为 4,所以这三个点必须分属三次扫描。下界与构造相等,最优答案为 3。
题意:记录按分数降序排列,同分时按姓名升序排列。哪一种两轮排序方法一定正确?
先按次关键字「姓名升序」排好,再按主关键字「分数降序」作稳定排序。
第二轮决定整体的分数顺序;对同分记录,它保留第一轮已经排好的姓名顺序。这正是选项 C。
A 保留的是姓名降序;B、D 最后一轮按姓名排序,会把姓名变成主关键字,不符合要求。
records.sort(key=lambda r: r.name)
records.sort(key=lambda r: r.score, reverse=True)以上是 Python 稳定排序的示意;也可以直接用复合关键字 (-score, name)。
题意:六项工作分别需要 2、3、4、7、8、10 分钟。三台工作站同时开始,每台必须依次处理恰好两项工作,求全部完成的最早时间。
把最长和最短的工作配对,可取:
| 工作站 | 两项任务 | 完成时间 |
|---|---|---|
| 1 | 10 + 2 | 12 |
| 2 | 8 + 3 | 11 |
| 3 | 7 + 4 | 11 |
于是 12 分钟全部完成。任何方案中,10 分钟的工作都必须与另一项配对,而最短另一项也要 2 分钟,故总完成时间至少为 12。构造达到下界。
题意:1 至 6 各出现一次。五个相邻位置的较大值依次为 4、4、6、6、5,求可能的排列数。
设六个位置为 a₁,…,a₆。
总数为 2×6=12。两类结构是 _ 4 _ 6 _ 5 与 _ 4 _ 6 5 _。已穷举 6!=720 个排列核验。
题意:长度 N 的二进制串初始全 0。一次操作选定一个位置,翻转其余所有位。对 1≤N≤100,有多少个 N 能变成全 1?
N 为奇数时不可能:每次翻转 N−1 个位,这是偶数个位。因此 1 的个数的奇偶性始终保持为偶数;目标全 1 含奇数个 1,不可达。这也包含 N=1。
N 为偶数时可以:把每个位置都选一次,总共 N 次。任意一个位只在选择自己时不翻转,合计翻转 N−1 次;该数为奇数,所以每个位最终都是 1。
恰好是 2、4、…、100,共 50 个。
题意:a=2026,b=27,依次执行 b *= a * b、b %= a,再输出 a+b。
a = 2026
b = 27
b *= a * b
b %= a
print(a + b)b *= a * b 等价于把旧 b 乘上 a * 旧b,所以 b 变成 2026×27²=1,476,954。它是 a 的整数倍,取余后 b=0,输出 2026。
题意:a=[4,5,3,1,0,2]。若 i%2==1 或 a[i]%2==1,则把 a[i] 加到 ans,求最终 ans。
| i | a[i] | i 为奇数? | 值为奇数? | 是否累加 |
|---|---|---|---|---|
| 0 | 4 | 否 | 否 | 否 |
| 1 | 5 | 是 | 是 | 是 |
| 2 | 3 | 否 | 是 | 是 |
| 3 | 1 | 是 | 是 | 是 |
| 4 | 0 | 否 | 否 | 否 |
| 5 | 2 | 是 | 否 | 是 |
累加 5+3+1+2=11。逻辑「或」允许任意一个条件成立,但两个条件都成立时仍只加一次。
题意:星期一到星期五每天至少吃一根香蕉,共吃八根;任意连续两天的总数不能为 3。求有序序列数。
先每天放 1 根,剩余 3 根按整数分拆分类。相邻和为 3 恰好对应相邻出现 1,2 或 2,1。
(2,3,1,1,1) 和 (1,1,1,3,2),共 2 种。总数为 5+2=7。完整列表:
1, 1, 1, 1, 4
1, 1, 1, 3, 2
1, 1, 1, 4, 1
1, 1, 4, 1, 1
1, 4, 1, 1, 1
2, 3, 1, 1, 1
4, 1, 1, 1, 1题意:整数输入 0≤x≤10000。cnt=0、i=1;当 cnt²≤x 时执行 cnt+=i、i+=1,最后输出 cnt。多少个 x 会输出 15?
cnt = 0
i = 1
while cnt * cnt <= x:
cnt += i
i += 1
print(cnt)cnt 的取值依次是三角数 0 → 1 → 3 → 6 → 10 → 15 → 21 → …。
10²≤x,即 x≥100。15²>x,即 x≤224。所以 x∈[100,224],个数为 224−100+1=125。x=225 会继续走到 21,不计入。
题意:1 至 6 的排列从第二项开始:大于此前所有值输出 L,小于此前所有值输出 S,其余输出 -。已知输出为 S-LLS,求排列。
| 项 | 值 | 与此前元素比较 | 输出 |
|---|---|---|---|
| 2 | 2 | 小于 4 | S |
| 3 | 3 | 介于 2 与 4 之间 | - |
| 4 | 5 | 大于此前全部 | L |
| 5 | 6 | 大于此前全部 | L |
| 6 | 1 | 小于此前全部 | S |
答案 4,2,3,5,6,1,已穷举确认唯一。
题意:N 初始为 5。把 +、−、×、÷ 各用一次,操作数 1、2、3、4 也各用一次;逐步从左到右运算,除法为精确数学除法,求最大结果。
可行构造:5 →(+3) 8 →(×4) 32 →(−2) 30 →(÷1) 30。
证明不能更大:设加数为 a,减数为 b,乘数为 m,除数为 d。所有操作数均为正数。
m(5+a)−b。a、b、m 是 2、3、4 的排列,最大值在 m=4、a=3、b=2 时取得,为 30。5m/d;加数 a 的贡献至多为 am;减数的贡献始终为负,忽略它只会提高上界。因此结果≤5m/d+am≤10+12=22,小于 30。这里 m≤4,且不同操作数的乘积 am≤4×3=12。故最大值为 30。另用精确分数穷举 4!×4!=576 种方案核验,避免浮点误差。
from itertools import permutations
from fractions import Fraction
best = Fraction(-10**9)
for ops in permutations("+-*/"):
for nums in permutations([1, 2, 3, 4]):
v = Fraction(5)
for op, n in zip(ops, nums):
if op == "+": v += n
elif op == "-": v -= n
elif op == "*": v *= n
else: v /= n
best = max(best, v)
print(best) # 30题意:1≤n≤10⁹。分别用三种方法补全函数,使其当且仅当 n 恰有一个非零十进制数位时返回 True。
def f(n: int) -> bool:
c = 0
while n > 0:
if n % 10 != 0: # G = n%10
c += 1
n //= 10
return c == 1n%10 取个位,n//=10 去掉个位,最终 c 就是非零位个数。
def g(n: int) -> bool:
while n % 10 == 0:
n //= 10
return n < 10 # H = n<10若原数形如 d×10ᵏ(1≤d≤9),去掉所有末尾 0 后只剩一位。反之,剩下两位或更多,且个位非零,就至少有两个非零位。题设 n≥1,因此无需处理 0 的死循环。
def h(n: int) -> bool:
p = 1
while n // p >= 10:
p *= 10
return n % p == 0 # I = n%p循环结束后,p 是不超过 n 的最大 10 的幂。例如 n=6000 时 p=1000。n%p=0 当且仅当最高位以下全为 0。单个位数时 p=1,余数也为 0,符合要求。
C++ 的三个填空与 Python 相同,依次为 n%10、n<10、n%p;原 C++ 框架的整数除法使用 / 和 /=。
题意:四层三角形圆圈最初全白,白色往左、黑色往右,球离开后颜色翻转。X 是第三层中间圆圈,出口编号 0 至 4;各小题独立重置。
X 初始白色,只在球经过它时翻色,所以从 X 出发的方向交替为 L、R、L、R、…。前 11 次经过有 6 次向左。这问的是「经过 X 的前 11 个球」,不是「总共落下的前 11 个球」。
顶点把 16 个球均分成 8、8;第二层两个点又各分成 4、4,因此第三层各点接收 4,8,4。中间 X 接收 8 个。
第三层的 4、8、4 均为偶数,各自均分后,第四层接收 2,6,6,2。第四层再均分,五个出口收到 1,4,6,4,1。所以出口 2 收到 6 个。
下表给出逐球模拟作交叉核验,L/R 分别表示左/右:
| 球编号 | 四层路径 | 出口 |
|---|---|---|
| 1 | LLLL | 0 |
| 2 | RLLL | 1 |
| 3 | LRRL | 2 |
| 4 | RRLR | 3 |
| 5 | LLRR | 2 |
| 6 | RLLL | 1 |
| 7 | LRRL | 2 |
| 8 | RRRL | 3 |
| 9 | LLLR | 1 |
| 10 | RLLR | 2 |
| 11 | LRRR | 3 |
| 12 | RRLL | 2 |
| 13 | LLRL | 1 |
| 14 | RLLR | 2 |
| 15 | LRRR | 3 |
| 16 | RRRR | 4 |
state = [[0] * (r + 1) for r in range(4)]
exits = [0] * 5
hits_x = 0
for _ in range(16):
c = 0
for r in range(4):
if r == 2 and c == 1:
hits_x += 1
direction = state[r][c] # 0 左,1 右
state[r][c] ^= 1
c += direction
exits[c] += 1
print(hits_x) # 8
print(exits) # [1, 4, 6, 4, 1]题意:程序 (1) 直接拆分 "12 34" 后输出 a+b;程序 (2) 拆分后用 map(int, ...) 转换,再输出 a+b。
# 程序 (1)
a, b = "12 34".split()
print(a + b) # 1234
# 程序 (2)
a, b = map(int, "12 34".split())
print(a + b) # 46split() 得到两个字符串,字符串的 + 是拼接;int 转换后,+ 才是数值加法。
题意:x=5,y=8;依次执行 x=x+y、y=x−y、x=x−y,最后 print(x,y)。
| 状态 | x | y |
|---|---|---|
| 初始 | 5 | 8 |
| x = x + y | 13 | 8 |
| y = x - y | 13 | 5 |
| x = x - y | 8 | 5 |
每一步都使用当前值,所以结果是 8 5。print 的默认分隔符是一个空格。
题意:a=[i+2 for i in range(10) if i%2==0],输出 sum(a)。
range(10) 给出 0 至 9;过滤后 i 为 0,2,4,6,8,各加 2 得到 [2,4,6,8,10]。
总和为 2+4+6+8+10=30。注意 range(10) 不包含 10,但计算后的元素可以等于 10。
题意:a=[1,2,3,4,5,6,7],idx=0、ans=0。重复五次:累加 a[idx],再把 idx 更新为 (idx+3)%len(a)。
| 第几轮 | 累加前 idx | a[idx] | 累计 ans | 更新后 idx |
|---|---|---|---|---|
| 1 | 0 | 1 | 1 | 3 |
| 2 | 3 | 4 | 5 | 6 |
| 3 | 6 | 7 | 12 | 2 |
| 4 | 2 | 3 | 15 | 5 |
| 5 | 5 | 6 | 21 | 1 |
累加的是 1+4+7+3+6=21。下标从 0 开始,并且先累加、后更新。
题意:a=[[0]*6]*7;执行 a[0][0]+=1 后,用两层循环累加全部 7×6 个位置,输出什么?
a = [[0] * 6] * 7
a[0][0] += 1
s = 0
for i in range(7):
for j in range(6):
s += a[i][j]
print(s) # 7外层的 *7 复制的是对同一个内层列表的引用。因此 a[0] 到 a[6] 都指向同一行;修改 a[0][0] 后,每一行看上去都是 [1,0,0,0,0,0]。累加时这一行被遍历 7 次,故结果为 7。
这不是把全部 42 个位置都改成 1。若需要 7 个独立的行,应写 [[0]*6 for _ in range(7)];但本题必须按原程序解读。
题意:ans=0;对 i in range(20),把 (i%5)%4 加到 ans,输出 ans。
i%5 的周期是 0,1,2,3,4,再取 %4 后的周期为 0,1,2,3,0,每周期和为 6。
0 至 19 恰有 4 个完整周期,总和为 4×6=24。不能把 (i%5)%4 简化成 i%4。
题意:a=[2,0,2,4,1,1,3];按 i=0,…,6 执行 a[i]+=a[6−i];随后依次输出各元素,end="",中间不加分隔符。
数组在原地更新。后半段读取前半段时,读到的是已经改过的值。
| i | 此次计算 | 更新后的数组 |
|---|---|---|
| 0 | 2+3=5 | [5, 0, 2, 4, 1, 1, 3] |
| 1 | 0+1=1 | [5, 1, 2, 4, 1, 1, 3] |
| 2 | 2+1=3 | [5, 1, 3, 4, 1, 1, 3] |
| 3 | 4+4=8 | [5, 1, 3, 8, 1, 1, 3] |
| 4 | 1+3=4 | [5, 1, 3, 8, 4, 1, 3] |
| 5 | 1+1=2 | [5, 1, 3, 8, 4, 2, 3] |
| 6 | 3+5=8 | [5, 1, 3, 8, 4, 2, 8] |
最终列表为 [5,1,3,8,4,2,8],拼接输出是 5138428。
题意:a=[7,13,2,18,3]、x=0。i 从 0 到 2026:偶数轮 x^=a[i%5],奇数轮 x^=a[4−i%5]。最后输出 x。
Python 的 ^ 是按位异或。满足 v^v=0、v^0=v,并具有交换律和结合律。
下标同时依赖 i%2 与 i%5,故以 10 为周期。前 10 轮的下标为:
0, 3, 2, 1, 4, 4, 1, 2, 3, 0每个下标恰好出现两次,所以一个完整周期的异或为 0。2027=202×10+7,只需看最后 7 轮,其下标为 0,3,2,1,4,4,1。
两次 4 和两次 1 抵消,剩下 a[0]^a[3]^a[2]=7^18^2。二进制为:
00111 (7)
10010 (18)
00010 (2)
-----
10111 (23)结果为 23,不是乘方运算。
题意:字符串 s 只含大写字母 I 和 O。框架中 I='I'、O='O',在 t.append(int( D )) 的 D 处填入表达式,使 I 转成整数 1、O 转成整数 0。
def convert(s: str) -> list[int]:
n = len(s)
t = []
I = 'I'
O = 'O'
for x in s:
t.append(int(x == I)) # D = x==I
return tx 为字母 I 时,比较结果为 True,int(True)=1;x 为字母 O 时,比较结果为 False,int(False)=0。例如 convert("IOI") 得到 [1,0,1]。
印刷题目使用的是字母 I 和 O;不是数字 1 和 0。直接 int(x) 会报错,字符串之间的按位 & 也不可用。
题意:carrot、cabbage 应输出 uhh;coconut、cheeseburger、cookies、curry、clams 应输出 yummy。在 print("yummy" if E else "uhh") 中补条件。
s = input()
print("yummy" if s[:2] != "ca" else "uhh")给定表中,恰好只有 carrot、cabbage 以 ca 开头。对它们条件为 False,走 else 分支;其余五项条件为 True,输出 yummy。
这是满足表中全部输入的一种答案,表达式不唯一,例如 not s.startswith("ca") 也满足给定表。题面没有定义表外单词应有的输出,不把这个前缀规则当成未给出的普遍规律。
题意:s、t 各自没有重复元素。补全 [x for x in s+t if F],返回只属于其中一个列表的元素,顺序不限。
def find_symmetric_difference(s: list, t: list) -> list:
return [x for x in s + t if (x in s) != (x in t)]两个布尔值不相等,恰好表示一真一假,也就是仅属于一个列表。
| x 属于 s | x 属于 t | 是否保留 |
|---|---|---|
| 是 | 否 | 是 |
| 否 | 是 | 是 |
| 是 | 是 | 否 |
| 否 | 否 | 否;且不会由 s+t 枚举到 |
列表各自无重复,所以仅在一边出现的元素也只会在 s+t 中出现一次。示例 s=[1,5,3]、t=[2,3,6] 得到 [1,5,2,6]。
两侧成员判断要保留括号,避免 Python 比较运算的链式解释。
题意:列表长度 1 至 1000,所有元素都是 −1000 至 1000 的非零整数。分别补全三种函数:仅当全正或全负时返回 True。
def check_same_sign1(a: list[int]) -> bool:
n = len(a)
pos = 0
neg = 0
for i in range(n):
if a[i] > 0:
pos += 1
else:
neg += 1
return pos == n or neg == n # G全部为正或全部为负即可。等价的短写法是 pos*neg==0;题设保证非空且没有 0。
def check_same_sign2(a: list[int]) -> bool:
n = len(a)
val = 0
for i in range(n):
val += a[i] * a[0] > 0 # H = a[0]
return val == n两个非零数乘积为正,当且仅当同号。固定与 a[0] 比较,所有 n 次都为真即同号。布尔值 True 累加为 1。不能填 a[i],那样只是在检查每个数的平方是否为正,混合正负也会错误通过。
def check_same_sign3(a: list[int]) -> bool:
n = len(a)
val = 0
for i in range(n):
val += a[i] / abs(a[i]) # I1 = a[i]
return val * val == n * n # I2 = val*val每项 a[i]/abs(a[i]) 等于 +1 或 −1。若正数有 p 个、负数有 q 个,val=p−q,n=p+q;只有 p=0 或 q=0 时,才有 val²=n²。
原框架这里是普通除法 /;本题商只有 ±1,且 n≤1000,累加结果可以精确表示,不影响平方比较。I1 与 I2 必须同时正确。
题意:一次操作把列表中某个位置的元素移到末尾,其他元素相对顺序不变。(a) 补全移动函数;(b) 判断等长列表 b 能否由 a 至多操作一次得到。
def move(a: list[int], p: int) -> None:
n = len(a)
x = a[p]
for i in range(p, n - 1):
a[i] = a[i + 1] # J = a[i+1]
a[n - 1] = x先保存 a[p],再把后面所有元素左移一格,最后把保存的 x 放到末尾。例如 [4,7,4,2] 的 p=1,得到 [4,4,2,7]。
def can_obtain(a: list[int], b: list[int]) -> bool:
n = len(a)
p = 0
while p < n and a[p] == b[p]:
p += 1
if p == n:
return True
for i in range(p, n - 1):
if a[i + 1] != b[i]: # K1
return False
return a[p] == b[n - 1] # K2!=。a[p]==b[n-1]。为什么有重复值时也能只检查 p?若某个可行操作移走了更早的 q<p,那么 q 到 p−1 这一段在移动后仍与原列表相等。这迫使 a[q]=a[q+1]=…=a[p]。从这一段中改为移走 a[p] 会得到相同结果,所以检查 p 不会漏解。若 q>p,则位置 p 根本不会改变,不可能修复首个不匹配。
算法时间 O(n),额外空间 O(1)。已对元素来自 {0,1,2}、长度 0 至 5 的全部 66,430 对等长列表与「枚举每个可移动位置」的结果交叉核验,包含重复元素、无需移动和单元素情形。
先从印刷题干手工推导,再用本地 Python 执行或穷举交叉检查。核验不是官方评分结果,也不替代对扫描题意的辨读。
重点复习:必要条件不能倒推;while 条件含等号时注意退出边界;列表重复可能共享引用;原地修改会影响后续读取;位运算 ^ 不是乘方;图上白色圆圈第一次走左支;固定参照比较符号,不能用自己乘自己。