Advent of Code 2025 / Everybody Codes 2025

第四年在這裡貼 Advent of Code 心得了。今年開始 Eric 決定要減輕一點他自己的負擔,所以題目數量減少到 12 天,不過仍然還是在 12/1 開場,每天開題;也就是今年只會到 12/12 就結束了。

目前看起來這個年末的程式設計謎題傳統有其他人繼承了下來,像是去年就開始的 Everybody Codes 它是自十一月的第一個星期一開始,連續四週的週一至週五各開一題——開啟時間似乎是 UTC+1 的 24:00,換算台灣時間是隔天的早上七點;麻煩的是這個時間剛好卡到我上班只好在到公司時偷一點時間出來寫。他這邊的故事風格比較中世紀一點,並且以鴨子做為各種吉祥物和故事元素,算是很不一樣的風格。

於是今年為了幫這篇文章充點篇幅 (?!),我也把 Everybody Codes 的題目心得寫在這裡。如果只要看 Advent of Code 的,點此連結跳到下面。(我有放防雷圖,應該是不必擔心這樣跳過去會被雷) 文章仍然押 12/1 發,也就是發出時 Everybody Codes 的心得已經寫好了。

以下 Everybody Codes 防雷圖。

2024 年全部解完會有一個很漂亮的齒輪動畫

Everybody Codes 2025

Quest 1: Whispers in the Shell (2025/11/3)

第一題果然是簡單的熱身題。讀字串陣列進來,然後照它說的動索引或動字串就行。輸入分析還是相對比較「麻煩」的部份,是個給新手練習輸入分析的題型。

Quest 2: From Complex to Clarity (2025/11/4)

這題其實是要我們算 Mandelbrot set。(第三題的故事部份也破了這個梗就是) 中間的計算基本上是為了讓參加者只用整數而做的,整體上其實等同於某個固定大小的定點數 (Part 1 是一位定點,Part 2/3 則是五位定點);所以我直接拿 C++ 的 <complex> 來做計算了。

Quest 3: The Deepest Fit (2025/11/5)

這題考的是能不能得出「排序是答案」這個結論:數字本身形成了一個 DAG,而答案即是這個 DAG 的拓撲排序。在看出相同大小元素對序列選取的影響之後也能容易得到 Part 3 的答案是眾數的個數。

Quest 4: Teeth of the Wind (2025/11/6)

齒輪模擬題,關鍵在於齒輪的不變量:固定時間內轉過的齒數。Part 3 的大小齒輪只是把這個不變量乘上大小變化倍率而已。

Quest 5: Fishbone Order (2025/11/7)

第一個週末題,不過仍然只是相對繁瑣一點的操作而已;比較搞事的是把數字串下來的部份是字串連接而已。

Quest 6: Mentorship Matrix (2025/11/10)

配對問題,問所有小寫字母跟其左邊的大寫字母組合有幾種。顯然在掃過去時有紀錄下目前為止的大寫字母數量就能馬上得出每個小寫字母有多少大寫字母配對;Part 3 就只是加上 sliding window 而已。

Quest 7: Namegraph (2025/11/11)

開始出現 DP 題了。前兩個 part 只是檢查,Part 3 就要實際算出生成的數量了;這裡就是標準的 DP,每個名字都能由較短的名字加上規則來組合而成,而我們只需要最後一個字母即可求出下一個長度的數量,因此 DP 陣列紀錄的就是每個長度每個字尾的名字個數。

Quest 8: The Art of Connection (2025/11/12)

這題算是簡化版計算幾何吧?不是對任何點做計算而是限定範圍內的點與直線交叉計算。也因為有限定所以交叉判定就沒有那麼多花俏的計算直接判斷大小就好。

Part 3 倒是有個很有趣的做法:與其對每種切法判斷線有沒有被切,反過來去累計這條線會被哪些切法切斷;後者不需要判斷,因為只要在線的兩邊各選一點就會被切到了。

說起來,今年有好多題目有彩蛋輸入;這題的「編織網」實際畫出來是可以看到有框成一個騎士盔甲的圖案在。

Quest 9: Encoded in the Scales (2025/11/13)

這題的配對關係很有意思。一個 (父,母,子) 的三元組,子的每個元素都是由父或母中其中而來的;前兩段就是要判斷任三個元素是否形成這樣的父母子三元組。仔細細想就能推斷出:

  • 所有元素,三人中至少要有兩人相同
  • 但兩同一不同的三種狀況至多只能出現兩種 (如 AAC/ACA/CAA 就不行)

Part 3 進一步要我們建出家族樹,但卻只要我們答出家族樹的總大小;因此這裡可以偷點懶,使用併查集 (disjoint set) 把所有有關係的三人全部加在一起,然後只要求出每個 set 的大小即可。

Quest 10: Feast on the Board (2025/11/14)

Part 1 和 Part 2 都是很容易的模擬題,照著它的說明下去模擬即可。

Part 3 就是難關了。給定一個盤面,求出所有走法使得所有羊棋被龍棋給吃光的序列數目。這個部份我用的是 BFS+DP,使用 BFS 依序搜過所有步數的所有狀態,然後在狀態更新時進行 DP 計算遊戲至下一步的序列數目,加去下一步的格子裡。基本上就是把 DP 中的「計算順序」以 BFS 來決定這樣。

Quest 11: The Scout Duck Protocol (2025/11/17)

這題開始有趣了。題目描述了一個「隊形平衡」演算法,要我們求出給定隊形到平衡需要多少步。Part 1 模擬十步,Part 2 解決一個小型問題,然後 Part 3 是個超大型隊形,直接跑會跑不完的那種。這題的關鍵在於:Part 3 輸入只需要進行演算法的第二部份,而這部份是有能快速求出步數的捷徑的。

Quest 12: One Spark to Burn Them All (2025/11/18)

給定一個數字陣列,對某一點「引爆」會將其所有往數字較小的路徑上的所有點一起引爆。Part 1 引爆左上角,Part 2 引爆左上右下兩個角,Part 3 則是要選擇三點能引爆最多格的點,求總計引爆多少格。是個很標準的淹水/BFS 題。

這題同樣有彩蛋輸入:Part 3 輸入所引爆的圖案是這個站的吉祥物鴨子。

Quest 13: Unlocking the Mountain (2025/11/19)

題目描述了一個奇妙的轉盤:轉盤上的數字自頂點起,第一個向右擺,下一個向左擺,再下一個向右擺,以此類推。數字擺好後,求出轉動某個格數後轉盤的位置為何。Part 1 每次只擺一個數字,Part 2 會擺一個範圍數字,Part 3 則是範圍變成超級大。陷阱其實就只有 Part 3 這個超大範圍,要你不要傻傻的一個一個數字擺下去而已。

Quest 14: The Game of Light (2025/11/20)

細胞自動機題。這題的規則比較有趣的是它看的是斜角相鄰的個數,不過實作上相對沒什麼問題——直到 Part 3 要你從中找循環為止。好在模擬起點是全黑,這表示 34×34 的空間可以切成對稱的四分之一來做模擬,然後可以發現它每 4095 步就循環一次 (這個循環可以偵測全亮的狀態來確認),所以要找出含特定中心樣式的步數就很容易了。

Quest 15: Definitely Not a Maze (2025/11/21)

確實不是迷宮啦,因為牆只有一條連續壁……不過看起來這條連續壁是逐漸往外繞的。題目要求從連續壁的頭直接走到尾,不過因為牆是這樣繞的關係,加上 Part 3 又是超大型連續壁,直接搜會狀態爆炸。這裡我用了座標壓縮 (又稱離散化) 的技巧,把整張地圖只留下牆跟牆的兩邊的座標,再丟去 A* 搜就搜出來了。

果然週末題都很有挑戰性……

Quest 16: Harmonics of Stone (2025/11/24)

給定指示序列,每個序列中的數字 n 表示在每第 n 格放一個石頭。Part 1 給定指示和總長度求出總石頭個數,Part 2 給定開頭求出指示,Part 3 則綜合起來,給定開頭求出給定量能放多長。

簡單想一想就能知道 Part 1 可以直接以總長度除以各指示間隔再求和,Part 2 則反過來,在第 N 格有這麼多石頭表示 N 的因數裡恰有這麼多個在指示當中,由此即可篩出有和沒有的指示。(當然這就假設沒有大於給定長度的指示,我們也無法知道有沒有)

Part 3 則很有意思。使用 Part 2 求出指示後,原本想說用個 LCM 看能不能找出循環簡化計算,結果作者放了好些大質數在裡面讓 LCM 直接翻車……。然後想說好吧,不然就二分搜一下看看,然後就想到這不就是標準的指數搜尋問題嗎?! 所謂指數搜尋,是指在一個右端無限的單調範圍內進行「二分」搜尋的演算法,由於第一步是依照指數成長的速度切段下來看要找的東西在不在裡面,故稱指數搜尋。因為切下來的段落是指數成長,若欲搜尋數在第 \(N\) 格,會需要 \(O(\log N)\) 次切到它;切下來的段落約有 \(N/2\) 個元素,進行標準二分搜也需要 \(O(\log N)\) 次,所以總計的時間就是 \(O(\log N)\)。

不過 Part 3 其實還有另解:由 Part 1,給定長度可以求出石頭數至多是長度乘上指示的倒數和,因此倒求時給定石頭數可以除以倒數和求得只低估少許的長度,再簡單的搜過去就能找出答案了。

Quest 17: Deadline-Driven Development (2025/11/25)

火山要爆發了!Part 1 和 2 熟悉火山岩漿的圓形範圍後,Part 3 要我們自一個起點開始,繞火山岩漿一圈回到原點,這當中不能讓火山岩漿的圓形範圍碰到走過的路線。

比較麻煩的是這個「繞火山岩漿一圈」這件事。我一開始的寫法是自火山口往左右拉橫線,搜尋時紀錄下這結果是走過哪半邊的橫線,然後去找各一邊的結果湊起來;不過後來看到另一個判定法是只拉半邊當作「換日線」,紀錄目前位置是從哪邊跨過「換日線」,然後就能以跨過某個方向一次的原點做為搜尋目標。

Quest 18: When Roots Remember (2025/11/26)

給定一些植物的「接線」,其結果會通過一條管線接到下一條植物上,求最終植物的結果——或是 Part 3 要你在一個給定 81 輸入的接線上求出最佳解。

這題這個接線方式其實很有類神經網路的味道:每個「植物節點」有輸入值,經過管線的加權和,到節點本身進行 (某個變形的) ReLU 整流後做為結果;但 Part 3 的最佳化並沒有一個目標值能讓我們做反向傳播。後來換了個思路,既然是在一個很高維度的空間做最佳化,那不如來寫模擬退火吧!不過這畢竟是我第一次在做這種最佳化題上寫模擬退火,有些細節並沒有那麼熟,加上這題的值空間有個很大的 0 谷,進入 0 谷就不容易跳出來,花了好些時間在處理這些地方。不過至少找到的最大值是正確的 (而且貌似不只一解,可能因為這些解的某些中間值不管怎麼調都是 0 的關係) 而且跑的夠快。

Quest 19: Flappy Quack (2025/11/27)

……標題和圖片根本就把這題破梗破光了 XD。不過意外的當數字很的時候不好做,你不可能用類 BFS 的搜尋搜過去:首先顯然不可能在每一步決定拍不拍,然後就算化簡到只在管子處算,這地圖目標之深會幾乎把所有淺層的狀態給找過,當淺層狀態夠多時一樣會爆炸。

不過如果觀察到拍的次數決定了到目標時的高度,那我們就可以不用管拍的次數了:反正能通過的範圍裡最低的那個就是最少次數。然後就是逐個掃過去決定哪些格子到得了,一路推到最後的管子就行了。

Quest 20: Dream in Triangles (2025/11/28)

這題就是標準的三角座標題,不過 Part 3 地圖還會轉……然後就忘記原地跳也是一步。orz

會轉的地圖一般來說有兩種解法:一種是真的把地圖轉過去,一種是倒過來把自己轉去目標格。這題因為是三角格的關係,需要的計算都不是很簡單,都會需要想一下。我是因為採用了轉地圖的方式才會忘記原地跳這一步;如果是自己轉去目標格的話應該是不會漏的。


以下 Advent of Codes 的防雷。

也是去年的 Advent of Code 十週年

Advent of Codes 2025

喔對了,Git repo 在這裡。今年我決定把一些東西升級成 C++20 了,例如尋徑函式庫的 State 類別條件的 concept 補了上去,這樣我要寫 State 時放個 static_assert 就能讓 clangd 幫我檢查是不是該實作的都寫了。

1. Secret Entrance

熱身題就是要有 off by one error 才能叫熱身 XD 倒是意外地和 Everybody 2025 的 Quest 1 有部份相同的元素在。(是什麼就不捏他了) 在寫 Part 2 的時候一直覺得怎麼左右不對稱很討厭,然後才發現對啊左右本來就不對稱:取餘的分界線是在 0 的左邊,所以往右轉時跨線是在踩 0 之前自然直接求,往左轉時跨線是在踩 0 之後所以才會差 1。

2. Gift Shop

果然這種問題就能看到兩種思考方式:一是掃過所有範圍看裡面數字是不是重覆字串,一是產生所有重覆字串再去看它們在不在範圍裡。考慮到這題的數字不會大到爆炸,在 9e15 上限之下重覆兩次的極限是七位數,也就是產生所有重覆字串頂多千萬出頭次,我就直接寫後者了。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料