← All Units← 返回单元列表 ← Course Hub← 课程主页
A P  C O M P U T E R  S C I E N C E  A
Unit 2 · Solutions第 2 单元 · 解析

Selection and Iteration — Solutions选择与循环 —— 解析

Companion to the AP-Style MC Practice SetAP 风格选择题练习的解析配套

MEDIUM HARD AP MC

Unit 2: Selection and Iteration2 单元:选择与循环CSA



MULTIPLE CHOICEWorked Answers详细解析

Multiple Choice — Worked Answers选择题(Multiple Choice)—— 详细解析

Each item restates the prompt and choices, marks the correct letter, and gives a brief justification. Trap distractors are called out where useful.每道题重述题干与选项、标出正确答案字母,并给出简明解析;对容易混淆的干扰项(trap distractor)单独提示。

Q1MEDIUMAP MCShort-Circuit &&

x = 0; if (x != 0 && 10/x > 0) … → result?x = 0if (x != 0 && 10/x > 0) … → 结果?

Answer:答案: (B)
&& short-circuits: if the left operand is false, the right operand is not evaluated.
  • x != 0 is false (since x = 0).
  • The whole && is therefore false; 10 / x is never computed.
  • The else branch runs: no is printed.
Trap (C): would only fire if the operands were evaluated left-to-right without short-circuit, which is not how Java's && works. This "guard-then-use" idiom is the standard way to protect against division-by-zero and null dereference.
&& 是短路求值(short-circuit evaluation):如果左操作数为 false,右操作数根本不会被计算
  • x != 0false(因为 x = 0)。
  • 整个 && 直接为 false10 / x 永远不会被求值。
  • else 分支执行:打印 no
陷阱 (C):只有在没有短路、按从左到右全部求值时才会触发——但 Java 的 && 并不是那样工作。这种"先保护、后使用"的写法是防止除以零和空引用解引用的标准技巧。
Q2MEDIUMAP MCfor Loop Count

for (int i = 5; i <= 20; i += 3) — count iterations?for (int i = 5; i <= 20; i += 3) —— 循环了多少次?

Answer:答案: (B)
Enumerate the values of i that satisfy i <= 20:
  • i = 5, 8, 11, 14, 17, 20 — six values
  • Next would be 23, which fails the test.
Formula: number of iterations = ⌊(end − start) / step⌋ + 1 when end is achievable. Here ⌊(20 − 5) / 3⌋ + 1 = 5 + 1 = 6. Trap (A) drops the inclusive endpoint; (C) over-counts by one (classic fencepost error).
列举所有满足 i <= 20i
  • i = 5, 8, 11, 14, 17, 20 —— 六个值
  • 下一个是 23,不再满足条件。
公式:当 end 恰好可达时,迭代次数 = ⌊(end − start) / step⌋ + 1。这里 ⌊(20 − 5) / 3⌋ + 1 = 5 + 1 = 6陷阱 (A) 漏掉了能取到的右端点;(C) 多算了一次(典型的"栅栏柱错误" fencepost error)。
Q3MEDIUMAP MCDe Morgan

Equivalent to !(a < b || c >= d)?!(a < b || c >= d) 等价的是?

Answer:答案: (B)
De Morgan: !(P || Q)!P && !Q. Negate each clause carefully:
  • !(a < b)a >= b (the negation of "strictly less" is "greater or equal," not "strictly greater")
  • !(c >= d)c < d
  • Join with &&: a >= b && c < d
Trap (A) drops the equality cases on the first clause. (C) keeps the || connective (forgot the second rule of De Morgan).
德摩根定律(De Morgan's Law):!(P || Q)!P && !Q。逐个否定每个子条件:
  • !(a < b)a >= b("严格小于"的否定是"大于等于",不是"严格大于")
  • !(c >= d)c < d
  • && 连接:a >= b && c < d
陷阱 (A) 漏掉了第一个子条件中的等号情形。(C) 没把 || 换成 &&(忘了德摩根的第二条规则)。
Q4HARDAP MCNested if Trace

x = 15; nested if printing A/B/C, then D always.x = 15;嵌套 if 可能打印 A/B/C,之后总会打印 D

Answer:答案: (B)
Walk through with x = 15:
  • x > 10true, enter outer if. The else if branch is now skipped entirely.
  • x % 2 == 015 % 2 = 1, so false. The inner else runs: print B.
  • Outside both ifs, println("D") runs: print D + newline.
Final output: BD. Trap (C) assumes else if (x > 5) also fires — but only one branch in an if / else if chain ever runs.
代入 x = 15 推演:
  • x > 10true,进入外层 ifelse if 分支现在整段被跳过
  • x % 2 == 015 % 2 = 1false。内层 else 执行:打印 B
  • 跳出两层 if 后,println("D") 执行:打印 D 并换行。
最终输出:BD陷阱 (C) 认为 else if (x > 5) 也会执行——但在 if / else if 链里,最多只会执行其中一个分支。
Q5HARDAP MCDigit Sum

n = 234; while (n > 0) { s += n % 10; n /= 10; }n = 234while (n > 0) { s += n % 10; n /= 10; }

Answer:答案: (B)
Standard "extract digits" idiom — % 10 grabs the last digit, /= 10 chops it off.
  • Iteration 1: 234 % 10 = 4s = 4; n = 23.
  • Iteration 2: 23 % 10 = 3s = 7; n = 2.
  • Iteration 3: 2 % 10 = 2s = 9; n = 0.
  • Condition n > 0 fails. Exit. Print 9.
Trap (D) is the reversed-digits answer — see Q9 for that pattern. Different idiom, different answer.
经典的"提取数字"惯用法 —— % 10 取最低位,/= 10 把它去掉。
  • 第 1 轮:234 % 10 = 4s = 4n = 23
  • 第 2 轮:23 % 10 = 3s = 7n = 2
  • 第 3 轮:2 % 10 = 2s = 9n = 0
  • 条件 n > 0 不再满足,退出。打印 9
陷阱 (D)数字反转的答案 —— 见 Q9 的同类模式。不同惯用法,不同答案。
Q6HARDAP MCString Traverse — Case Trap

s = "ProgrAmming"; count chars equal to 'a'/'e'/'i'/'o'/'u'.s = "ProgrAmming";统计等于 'a'/'e'/'i'/'o'/'u' 的字符个数。

Answer:答案: (B)
Comparing chars with == is case-sensitive: 'A' == 'a' is false. Walk through the string:
 P  r  o  g  r  A  m  m  i  n  g
 0  1  2  3  4  5  6  7  8  9 10
Lower-case vowels only: o at 2, i at 8. Count = 2. The uppercase A at index 5 is not matched.

Trap (C) counts the uppercase A by treating the comparison as case-insensitive. To make this counter case-insensitive you'd normalize: Character.toLowerCase(c) == 'a' || …, or call s.toLowerCase() first.

== 比较 char区分大小写的case-sensitive):'A' == 'a'false。逐字符走一遍:
 P  r  o  g  r  A  m  m  i  n  g
 0  1  2  3  4  5  6  7  8  9 10
只匹配小写元音:o(下标 2)、i(下标 8)。计数 = 2。下标 5 处的大写 A 会被匹配。

陷阱 (C) 把大写 A 也算上,相当于把比较当成了不区分大小写。要做成不区分大小写,可以先归一化:Character.toLowerCase(c) == 'a' || …,或先调 s.toLowerCase()

Q7HARDAP MCwhile — Flag-controlled exit + counter

n=100; count=0; stop=false; while (n>0 && !stop) { count++; if (count>5) stop=true; n-=10; } println(count + " " + n);n=100; count=0; stop=false; while (n>0 && !stop) { count++; if (count>5) stop=true; n-=10; } println(count + " " + n);

Answer:答案: (C)
Two things move inside the body: count increments first; n decrements last. The flag stop is set inside the body, but the loop condition only re-checks after the body completes. So the iteration where count first exceeds 5 still finishes — including the n -= 10.
iter | count after  stop after  n after
  1  |     1         false        90
  2  |     2         false        80
  3  |     3         false        70
  4  |     4         false        60
  5  |     5         false        50
  6  |     6         true         40   ← count>5 fires, stop set, n still drops
loop test now: 40>0 && !true = false → exit
Print: "6 40".

Trap (A) assumes stop=true short-circuits the rest of the body (it doesn't — if/then isn't break). (B) stops after the 5th decrement and before the count increment. (D) ignores the flag entirely.

循环体内有两个变量在变:count 先自增,n 最后再减。标志变量 stop 是在循环体内部被赋值的,但循环条件要等整个循环体执行完才重新检查。所以当 count 第一次超过 5 那一轮,整个循环体仍会跑完——包括 n -= 10
iter | count after  stop after  n after
  1  |     1         false        90
  2  |     2         false        80
  3  |     3         false        70
  4  |     4         false        60
  5  |     5         false        50
  6  |     6         true         40   ← count>5 触发,stop 置真,但 n 仍然下降
loop test now: 40>0 && !true = false → 退出
打印:"6 40"

陷阱 (A) 错以为 stop=true 会"短路"掉循环体后续语句(不会——if/then 不是 break)。(B) 在第 5 次减法之后、count 自增之前停止。(D) 完全忽略了标志变量。

Q8HARDAP MCNested Loop Count

for i = 1..4 { for j = 1..i { count++; } }for i = 1..4 { for j = 1..i { count++; } }

Answer:答案: (B)
The inner loop runs i times for each outer i. Total iterations:
  • i = 1: inner runs 1 time
  • i = 2: inner runs 2 times
  • i = 3: inner runs 3 times
  • i = 4: inner runs 4 times
Total = 1 + 2 + 3 + 4 = 10. (This is the triangular number n(n+1)/2.)

Trap (C) assumes a full 4 × 4 rectangle (which would happen if the inner loop were j <= 4 independent of i). (A) counts only outer iterations.

对每个外层 i,内层循环跑 i 次。合计:
  • i = 1:内层跑 1 次
  • i = 2:内层跑 2 次
  • i = 3:内层跑 3 次
  • i = 4:内层跑 4 次
合计 = 1 + 2 + 3 + 4 = 10。(这是三角数n(n+1)/2。)

陷阱 (C) 把它当成完整的 4 × 4 矩形(如果内层是 j <= 4i 无关,才会是 16)。(A) 只数了外层的次数。

Q9HARDAP MCReverse Digits

n = 1234; while (n > 0) { rev = rev*10 + n%10; n /= 10; }n = 1234while (n > 0) { rev = rev*10 + n%10; n /= 10; }

Answer:答案: (B)
The recurrence rev = rev * 10 + (last digit of n) "shifts" the previously-accumulated digits left and appends the next digit. Trace:
  • Start: n = 1234, rev = 0
  • Iter 1: rev = 0·10 + 4 = 4; n = 123
  • Iter 2: rev = 4·10 + 3 = 43; n = 12
  • Iter 3: rev = 43·10 + 2 = 432; n = 1
  • Iter 4: rev = 432·10 + 1 = 4321; n = 0 — exit.
Final rev = 4321. Trap (C) stops one iteration early; (D) forgets the + n%10 on the last step.
递推 rev = rev * 10 + (n 的最低位) 把已积累的数字"整体左移一位",然后把新的最低位接到末尾。逐轮推演:
  • 初始:n = 1234, rev = 0
  • 第 1 轮:rev = 0·10 + 4 = 4n = 123
  • 第 2 轮:rev = 4·10 + 3 = 43n = 12
  • 第 3 轮:rev = 43·10 + 2 = 432n = 1
  • 第 4 轮:rev = 432·10 + 1 = 4321n = 0 —— 退出。
最终 rev = 4321陷阱 (C) 提前一轮停止;(D) 漏掉了最后一步的 + n%10
Q10HARDAP MCwhile — OR + guarded decrements

a=5; b=3; while (a>0 || b>0) { if (a>0) a--; if (b>0) b--; } println(a + " " + b);a=5; b=3; while (a>0 || b>0) { if (a>0) a--; if (b>0) b--; } println(a + " " + b);

Answer:答案: (A)
Two subtle things: (1) the loop condition is ||, so the loop runs while either variable is still positive; (2) the inner if guards prevent b from going negative once it hits 0.
iter | a after  b after  loop test after
  1  |    4        2     4>0 || 2>0 → true
  2  |    3        1     true
  3  |    2        0     true
  4  |    1        0     (b guard skipped) true
  5  |    0        0     0>0 || 0>0 → false → exit
Once b = 0, the if (b > 0) guard skips its decrement — so b stays at 0 while a finishes counting down.

Trap (B) drops the if (b>0) guard, so b goes negative (decrements once per iteration past 0). (C) swaps the roles. (D) assumes the loop can't terminate because of the OR — but eventually both reach 0 since each iteration strictly decreases the positive variable(s).

两个细节:(1) 循环条件是 ||,所以只要任何一个变量还为正,循环就继续;(2) 内部的 if 保护(guard)让 b 在到达 0 后不会变成负数。
iter | a after  b after  loop test after
  1  |    4        2     4>0 || 2>0 → true
  2  |    3        1     true
  3  |    2        0     true
  4  |    1        0     (b 的保护跳过自减) true
  5  |    0        0     0>0 || 0>0 → false → 退出
一旦 b = 0if (b > 0) 保护就会跳过它的自减——所以 b 停在 0,而 a 继续往下数。

陷阱 (B) 没有 if (b>0) 保护,b 会变负(每轮在 0 之后还减一次)。(C) 把两者的角色搞反了。(D) 以为 OR 让循环停不下来——其实只要正值变量每轮严格减少,最终都会到 0。

Q11HARDAP MCwhile Threshold

n = 0; p = 1; while (p < 1000) { p *= 2; n++; }n = 0; p = 1; while (p < 1000) { p *= 2; n++; }

Answer:答案: (B)
We count how many doublings of 1 are needed to reach or exceed 1000.
iter | p after body   n
  1  |   2            1
  2  |   4            2
  3  |   8            3
  4  |  16            4
  5  |  32            5
  6  |  64            6
  7  | 128            7
  8  | 256            8
  9  | 512            9
 10  | 1024          10
loop test: 1024 < 1000 false → exit
Note: the test p < 1000 uses the value of p before the next iteration. p = 512 is still < 1000, so the body runs once more — that final iteration is what pushes n from 9 to 10. Trap (A) stops at p = 512 without doing the final iteration; (D) reports p instead of n.
数一数把 1 翻倍多少次才能达到或超过 1000:
iter | p 执行后   n
  1  |   2        1
  2  |   4        2
  3  |   8        3
  4  |  16        4
  5  |  32        5
  6  |  64        6
  7  | 128        7
  8  | 256        8
  9  | 512        9
 10  | 1024      10
loop test: 1024 < 1000 false → 退出
注意:循环条件 p < 1000 用的是下一次循环开始之前pp = 512 仍然 < 1000,所以循环体还要再跑一次——正是这最后一次把 n 从 9 推到 10。陷阱 (A)p = 512 处停下,没做最后那一轮;(D) 报的是 p 的值而不是 n
Q12HARDAP MCfor — Integer-division step (log₂ counter)

n=100; count=0; for (int i=n; i>1; i=i/2) count++; println(count);n=100; count=0; for (int i=n; i>1; i=i/2) count++; println(count);

Answer:答案: (B)
The update step i = i / 2 is integer division, so values shrink fast and round down. The condition is i > 1 (not > 0), so the loop exits once i reaches 1.
iter | i before  i after = i/2  count after
  1  |   100        50                1
  2  |    50        25                2
  3  |    25        12                3
  4  |    12         6                4
  5  |     6         3                5
  6  |     3         1                6
loop test: 1 > 1 false → exit
So count = 6 — equivalently, ⌊log₂(100)⌋ = 6 (since 2⁶ = 64 ≤ 100 < 128 = 2⁷).

Trap (A) stops when i reaches 2 (off-by-one on the loop bound). (C) counts the would-be iteration at i = 1. (D) mistakes the loop for one that runs n/2 times.

更新步 i = i / 2整除,所以值缩小很快、还会向下取整。退出条件是 i > 1(不是 > 0),所以 i 到 1 就退出。
iter | i 执行前   i 执行后 = i/2  count 执行后
  1  |   100        50                1
  2  |    50        25                2
  3  |    25        12                3
  4  |    12         6                4
  5  |     6         3                5
  6  |     3         1                6
loop test: 1 > 1 false → 退出
所以 count = 6 —— 等价于 ⌊log₂(100)⌋ = 6(因为 2⁶ = 64 ≤ 100 < 128 = 2⁷)。

陷阱 (A)i 到 2 时就停(循环边界算错了一)。(C) 多算了 i = 1 时那一轮。(D) 把循环误当作要跑 n/2 次。

Q13HARDAP MCif-else-if chain vs independent ifs

x=7; result=""; if(x>0) result+="P"; if(x%2==0) result+="E"; else if(x>5) result+="B"; if(x<10 && x>3) result+="M"; println(result);x=7; result=""; if(x>0) result+="P"; if(x%2==0) result+="E"; else if(x>5) result+="B"; if(x<10 && x>3) result+="M"; println(result);

Answer:答案: (A)
The structure is three separate if statements — but the second one has an attached else if. That changes its behavior: the chain runs at most one of its branches; the independent ifs each run on their own merits.
  • 1st if (independent): x > 07 > 0 true → append "P". Result: "P".
  • 2nd if/else if (chain): x % 2 == 07 % 2 = 1 false → check else if (x > 5) → true → append "B". The "E" branch is skipped and so is any sibling once one branch fires. Result: "PB".
  • 3rd if (independent): x < 10 && x > 3 → both true → append "M". Result: "PBM".
Print: "PBM".

Trap (B) "PEBM" treats the chain as two independent ifs (both E and B branches appended). (C) ignores the final independent if. (D) sees the chain correctly but skips both its branches (forgetting else if).

结构是三条独立的 if 语句——但第二条带了一个 else if。这就改变了它的行为:链式结构里最多只执行一个分支;独立的 if 各自按条件独立判断。
  • 第 1 条 if(独立):x > 07 > 0 true → 追加 "P"。结果:"P"
  • 第 2 条 if/else if(链式):x % 2 == 07 % 2 = 1 false → 检查 else if (x > 5) → true → 追加 "B""E" 分支被跳过——同一个链里一旦有分支命中,剩下的分支都不会运行。结果:"PB"
  • 第 3 条 if(独立):x < 10 && x > 3 → 两个都为 true → 追加 "M"。结果:"PBM"
打印:"PBM"

陷阱 (B) "PEBM" 把链式当作两个独立 ifEB 两个分支都追加)。(C) 忽略了最后那条独立 if(D) 链式判断正确但跳过了两个分支(忘了 else if)。

Q14HARDAP MCNested loop — modular pair counting

total=0; for(i=1..6) for(j=i..6) if((i+j)%3==0) total++; println(total);total=0; for(i=1..6) for(j=i..6) if((i+j)%3==0) total++; println(total);

Answer:答案: (B)
The inner loop starts at j = i (not 1), so we only count unordered pairs (i, j) with i ≤ j ≤ 6 where i + j is divisible by 3. Walk row by row:
i=1, j∈{1..6}: hits at j=2 (sum 3), j=5 (sum 6)             → 2
i=2, j∈{2..6}: hits at j=4 (sum 6)                          → 1
i=3, j∈{3..6}: hits at j=3 (sum 6), j=6 (sum 9)             → 2
i=4, j∈{4..6}: hits at j=5 (sum 9)                          → 1
i=5, j∈{5..6}: no hits (sums 10, 11)                        → 0
i=6, j=6:      hits at j=6 (sum 12)                         → 1
                                                  total = 2+1+2+1+0+1 = 7
Print: 7.

Trap (D) 12 counts the full 6×6 grid's hits (would be right if inner ran j = 1..6 — double-counts unordered pairs and includes ordered duplicates). (A) 6 miscounts a row. (C) 8 double-counts a single pair.

内层循环从 j = i 而不是 1 开始,所以只数无序对 (i, j)(满足 i ≤ j ≤ 6)且 i + j 能被 3 整除的情况。逐行扫描:
i=1, j∈{1..6}: 命中 j=2(和 3)、j=5(和 6)            → 2
i=2, j∈{2..6}: 命中 j=4(和 6)                          → 1
i=3, j∈{3..6}: 命中 j=3(和 6)、j=6(和 9)             → 2
i=4, j∈{4..6}: 命中 j=5(和 9)                          → 1
i=5, j∈{5..6}: 没有命中(和为 10、11)                   → 0
i=6, j=6:      命中 j=6(和 12)                          → 1
                                                  total = 2+1+2+1+0+1 = 7
打印:7

陷阱 (D) 12 是把整个 6×6 网格的命中都数上(相当于内层跑 j = 1..6——把无序对重复计数)。(A) 6 某一行数错了。(C) 8 多算了一对。

Q15HARDAP MCwhile — Conditional shrink (halve/decrement)

n=13; count=0; while(n>1) { if(n%2==0) n/=2; else n--; count++; } println(count);n=13; count=0; while(n>1) { if(n%2==0) n/=2; else n--; count++; } println(count);

Answer:答案: (B)
Each iteration either halves n (when even) or subtracts 1 (when odd). count tracks every iteration, not just halvings. Trace from n = 13:
iter | n before  branch   n after  count after
  1  |    13      else      12          1     (odd: decrement)
  2  |    12      if         6          2     (even: halve)
  3  |     6      if         3          3     (even: halve)
  4  |     3      else       2          4     (odd: decrement)
  5  |     2      if         1          5     (even: halve)
loop test: 1 > 1 false → exit
Print: 5.

Trap (A) 4 miscounts by 1 — usually skipping the first "odd → 12" iteration. (C) 6 runs one extra loop (forgets the exit is at n = 1, not 0). (D) 12 counts the value of n after one decrement instead of count.

每轮要么把 n 减半(偶数时),要么减 1(奇数时)。count 记录的是每一轮的次数,而不仅仅是减半的次数。从 n = 13 推演:
iter | n 执行前   分支     n 执行后   count 执行后
  1  |    13      else        12          1     (奇数:减 1)
  2  |    12      if           6          2     (偶数:减半)
  3  |     6      if           3          3     (偶数:减半)
  4  |     3      else         2          4     (奇数:减 1)
  5  |     2      if           1          5     (偶数:减半)
loop test: 1 > 1 false → 退出
打印:5

陷阱 (A) 4 数错了一次——通常是漏掉第一轮"奇数 → 12"。(C) 6 多跑了一轮(忘了退出条件是 n = 1,不是 0)。(D) 12 把一次减 1 后的 n 当作了答案,没看 count