Zapping
頻道是 100 個點圍成的環,兩點距離為 d = |a − b|,順逆兩個方向分別是 d 和 100 − d,取小的即為答案。
| Attempts | 2 |
|---|---|
| First attempt | 2026-09-23 |
| Solved | 2026-09-23 |
Problem
題目的主角很喜歡看電視,不過他從來不會乖乖停在同一台,會一直切到其他台。偏偏他家的狗把遙控器啃了,數字鍵全滅,只剩下「下一台」(△)和「上一台」(▽)兩顆按鈕還能用。想從第 3 台轉到第 9 台嗎?乖乖按 △ 六次吧。🐶🥵
而電視總共有 100 個頻道,編號 0 到 99,而且是循環的:
-
在 99 台按 △ 會回到 0 台。
-
在 0 台按 ▽ 會跳到 99 台。
給定現在的頻道 和想去的頻道 ,求最少要按幾下。
輸入有多組測資(最多 200 組),每行兩個整數 、,,以一行 作為結束字元。
Approach
我一看到就把它畫成數線:、 是 0–99 上的兩個點,想從 走到 只有兩條路:
-
直接走:從 走到 ,距離 。
-
繞過去:往反方向走到盡頭,從 99 跳回 0(或反過來),再走到 。
關鍵在於第二條路那個「跳回去」的動作也要按一下。把數線頭尾接起來看就很清楚:
四段加起來是 ,正好是 100 個頻道。數線上 0 到 99 只有 99 個間隔,第 100 個間隔就是那條虛線,也就是環的接縫。
所以令 ,兩個方向的按鍵數分別是 和 ,答案就是
因為取了絕對值, 還是 都一樣,完全不用像我一開始那樣 swap。
什麼時候該繞路?
兩條路一條走 格、一條走 格,加起來永遠是 100,也就是剛好一整圈。所以兩條路不可能同時都很長,一邊變長,另一邊就等量變短。(呈線性負相關)
要判斷繞路是否比較近,只要解這個不等式:
換成白話:
| 間距 | 比較 | 該走哪條 |
|---|---|---|
| (不到半圈) | 直接走 | |
| (剛好半圈) | 兩條一樣長 | |
| (超過半圈) | 繞過去 |
所以 50 就是分界點,也就是一圈 100 的一半。這個分界也直接解釋了我第一版為什麼只有 會錯:舊公式用的是 ,等於把一圈當成 99,分界點被偷偷挪到 。
- 時兩個版本都選「直接走」,答案都是 ,錯誤完全被藏住。
- 時舊版改選「繞過去」,而繞過去那條剛好少算了一格,錯誤才浮上來。
另一種寫法
如果想直接模擬「往上按幾下」和「往下按幾下」,可以用取模。往上是從 走到 ,往下是從 走回 :
int up = (b - a + 100) % 100; // 一直按 △int down = (a - b + 100) % 100; // 一直按 ▽cout << min(up, down) << '\n';先加 100 是因為 C++ 對負數取模會得到負數(例如 ),加完再取模就能保證落在 到 。而 up + down 在 時一定等於 100,跟上面的 和 是同一件事。
Wrong attempts
第一版我先 swap 讓 ,然後另一條路寫成 :
while (cin >> a >> b) { if (a == -1 && b == -1) { break; } if (a > b) { swap(a, b); }
if (a == 0 && b == 99) { cout << "1\n"; } else { cout << min(b - a, 99 - b + a) << '\n'; }}我少算了 99 → 0 那一格。題目寫的是 0 到 99,我腦中就直接拿 99 當一圈的長度,忘了它其實有 100 個頻道,太粗心了。😂
更好笑的是第 9–10 行那個特判。範例的 用我的公式算出來是 ,明顯不對,我就直接寫死輸出 1。其實那是公式在報錯,我卻直接忽略了。它剛好是 的情況,少算一格的錯誤在這裡非常明顯,真的是事出必有因。
實際上只要間距 ,繞過去那條就會變成比較短的一邊,少的那一格就會直接反映在答案上:
| 正確 | 我的 | 結果 | |||
|---|---|---|---|---|---|
| 3 | 9 | 6 | 6 | 6 | ✅ |
| 12 | 27 | 15 | 15 | 15 | ✅ |
| 0 | 99 | 99 | 1 | 0 → 特判成 1 | ✅ (但是是被特判蓋掉) |
| 0 | 49 | 49 | 49 | 49 | ✅ |
| 0 | 50 | 50 | 50 | 49 | ❌ |
| 10 | 80 | 70 | 30 | 29 | ❌ |
三筆範例全過,其中一筆還是靠特判撐住的,難怪自己測不出來。WA 之後我想到 這組,果然跑出 49,才發現是 99 和 100 的差別。
改完的差異只有這幾行:
if (a > b) { swap(a, b);}d = abs(a - b);if (a == 0 && b == 99) { cout << "1\n";} else { cout << min(b - a, 99 - b + a) << '\n';cout << min(d, 100 - d) << '\n';swap 和特判都不見了,特判能被刪掉,本身就是公式終於對了的證據,程式碼本身也優雅簡潔了許多(可讀性也更高)!
Solution
6 collapsed lines
#include <iostream>#include <algorithm> // min#include <cstdlib> // abs
using namespace std;
void solve() { int a, b; int d;
while (cin >> a >> b) { if (a == -1 && b == -1) { break; }
d = abs(a - b);
cout << min(d, 100 - d) << '\n'; }}
8 collapsed lines
int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
solve();
return 0;}被標起來的兩行就是全部:算出兩點距離,再跟繞一圈的另一邊比大小。
Sample Input
3 90 9912 27-1 -1Sample Output
6115驗算: 的 ,另一邊是 ,答案 1,不需要任何特判。
自己加的測資
0 5050 07 799 080 10-1 -150500130分別測了剛好一半、、原地不動、繞一圈、以及 又要繞路的情況。
錯誤歷程
- WA繞一圈的另一邊用 99 去扣,少算了 99 → 0 那一格;還特判了 0 99 把錯誤蓋掉#1
- AC改成 diff = |a − b|,答案 min(diff, 100 − diff),也不用再 swap 一次#2
See also
-
Cycle graph(Wikipedia):環圖 的定義與性質
-
Lee distance(Wikipedia):同一個距離公式在 Coding theory 裡的版本