Advent of Code 2022

一年一度的 AoC 又要開始了。

為還不知道 AoC 是什麼的人簡單介紹一下:Advent of Code,名字取自聖誕日曆 Advent Calendar,是每年 12 月時開始的程式設計解題挑戰。和聖誕日曆一樣,從 12 月 1 日開始每天都有新題目,一直到 12 月 25 日聖誕節為止。每天更新的時間是美東時間的換日,換算台灣時間是下午一點。

題目的故事主題當然就是聖誕節,基本上就是那種「聖誕節快完啦,幫忙寫些程式救救聖誕節」之類的故事;但題目本身則包羅萬象,從簡單的練習題到複雜的微組合語言模擬程式都有,很適合各種不同程度的程式設計練習。就我看來,初階的程式設計學習者應該至少前五到十天不會有問題,有學了一些資料結構演算法的應該可以做到 15 到 20 日,而全通雖然是點挑戰,但也不需要到我這種打過比賽或寫過一堆程式的人,大概資工系大二到大三左右的程度應該就行了吧?

我自己其實曾經在 2018 年時做過一次,不過那年因為各種原因停在半途,一直到去年看到一個我加的 Discord 群裡有要做就又把它撿回來了。去年因為比較閒一點,除了去年當年的題目之外,一路回頭到把 2018 年之後的題目也做完了;今年則是在約一個月前把最早的三年 (2015~2017) 也清掉加入 350 星俱樂部。

這篇文章預計會是動態更新,發表時間是押 12 月 1 日,但會隨著我每天做題目新增當天的簡評。不過鑑於文章更新也要時間,當天的題目可能要到晚上或隔天才會更新。原始碼整理在 github 上了。

繼續閱讀以下就是各天的題目了。

Day 1: Calorie Counting

果然 AoC 最大的敵人是分析輸入。我一個月前在寫時慢慢累積了一套專門分析輸入的函式,隨著時間發展從切行支援到切字元,那因為分界字元時常會連續所以加了把空字串給丟掉的邏輯,偏偏這一題的空白行分界是有意義的……

除此之外就只是個很普通的初學練習題而已。果然連第一天都不讓人好過是嗎……

Day 2: Rock Paper Scissors

字很多,但基本上就是玩猜拳而已。所以我就寫出了這種程式:

……好孩子請不要學。這個的邏輯是把三種拳當做三進位,兩個拳相減就是輸贏。細說起來是這樣子:

我\對方0 石頭1 布2 剪刀
0 石頭1 平手 = (0-0+4)%30 輸 = (0-1+4)%32 贏 = (0-2+4)%3
1 布2 贏 = (1-0+4)%31 平手 = (1-1+4)%30 輸 = (1-2+4)%3
2 剪刀0 輸 = (2-0+4)%32 贏 = (2-1+4)%31 平手 = (2-2+4)%3

第二部份就是倒過來把減變成加,就能從對方出拳跟結果倒推出自己的出拳了。

Day 3: Rucksack Reorganization

找出共同字元的題目。所以我一開始用了 find_first_of 來做,然後第二部份要我們做三個字串的共同字元……結果還是逃不掉要寫集合交集。(然後才發現原來 STL 有 std::set_intersection (倒地

題外話,聽說這一題有人用 OpenAI 在公佈後十秒送出答案……我不予置評就是。

Day 4: Camp Cleanup

有個早該寫起來的函式庫馬上咬我了……看到有人的 AoC 的函式庫有一個是把一行中所有數字拆出來的,這個用在這題上就很剛好了。所以我決定這天的程式碼等我把這個函式補進去之後再丟上來——今天因為正好去考日檢,是在考場外迅速解決完之後繼續回頭考前惡補的。

除此之外就是個很單純的範圍包含/相交練習題,對於沒有做過/沒有想過的參賽者來說確實是個小挑戰;我在的那個 DC 群裡就有人很直覺地把兩個範圍的數字給列舉出來再來比較。以一個首週週末的題目來說有這個另解確實頗讚。

Day 5: Supply Stacks

……今年是專出輸入分析題嗎 (倒地)

然後又是函式庫搞事:我忘了我的簡單輸入會把行首行尾的空白給去掉……。好在我頗早就留了一個什麼都不處理全部吃進來的函數,原本是用來輸入 ASCII 迷宮圖用的正好派上用場;昨天要補的拆出數字的函數也用上了,後半段的指令直接叫用省去有的沒的分析。

實際要做的事倒是很簡單的模擬而已。原本我看到開始是堆疊操作還用了 std::stack 的,結果看到後半是一次全搬果斷把它換回 std::vector。不過這樣一來兩個部份的程式碼差別意外的少,大概只有半行左右吧?

Day 6: Tuning Trouble

然後馬上來了個零顆星的題目了。

然後我又因為 off by one 吃了一次 WA。然後在看到後半部份要做 14 個字時想回頭打臉兩分鐘前手刻六個條件的自己。(嘆氣)

理論上弄一點技巧是可以做成 O(n) 而不是 O(nk²) 啦,但就懶……

Day 7: No Space Left On Device

前一天還在 reddit 上看到有人整理往年的首殺時間,發現到平均來說第一次的難度陡升出現在第七天;結果今天就來了一個標準的遞迴結構練習題了。

既然是標準遞迴結構,那對遞迴熟的人來說也就沒什麼太大問題了。對初學者來說的崁大概就是搞懂這整個遞迴結構吧;沒碰過的人應該有一點難想像自己包含自己的資料結構到底是什麼鬼。好在這題用的是資料夾結構這個最好懂的遞迴/樹狀結構當題目,不然我覺得這題應該可以往後移一週左右。

EDIT: 結果看了看討論,新手掉的坑果然不是結構本身,而是被多個路徑中的同名資料夾給混淆了。

Day 8: Treetop Tree House

這題題目一出來馬上有種強烈的 Skyscraper 數獨題的感覺。所以我的第一部份做法就是跟那些題目一樣,從邊界看進去去數有哪些是看得到的。

第二部份則是倒過來要決定看出去看得到的範圍。理論上是有 O(n²) 的做法啦,不過由於頗為繁瑣 (大概就是那些高效字串比對演算法那種繁度) 且題目輸入本來就不大,所以就直接寫了個 O(n³) 的直覺做法了。

意外的是個兩部份差不少的題目。

Day 9: Rope Bridge

可能是我看 Alex Diener 很久了吧,看到這題的小繩子亂跑我馬上想到的是他常玩的 Deadly Room of Death 裡的一個叫 Gentryii 的東西;這東西的形象參考自瑪利歐系列的鏈條怪,就是一個會咬人的大頭跟它拖在身後的一串鏈子。遊戲裡的它跟這題裡一樣,頭和身體的各節都各自佔一格,也有幾乎一模一樣的移動規則——我如果久遠之前看過有人解說過沒記錯的話,移動規則差別除了遊戲裡頭無法和鏈條重疊之外,應該只有在斜角時鏈條有個強烈傾向沿著原路移動的特性;這裡的繩子由於必定走斜角,會一口氣全部滑到旁邊去,這由第二部份的範例應該就看得出來。

話說回題目。這裡有一個稍微比較值得一提的地方是題目的主幹:尾巴節的跟隨邏輯。原本我以為我這裡的邏輯會寫好多行 if-else,但邊想邊寫邊化簡的結果最後這裡的邏輯竟然只有五行搞定:

void tailMove(pair<int, int> head, pair<int, int> &tail)
{
	if(abs(head.first - tail.first) <= 1 && abs(head.second - tail.second) <= 1) return;
	if(tail.first > head.first) tail.first--;
	else if(tail.first < head.first) tail.first++;
	if(tail.second > head.second) tail.second--;
	else if(tail.second < head.second) tail.second++; 
}

事情是這樣的:注意到在要斜角移動的狀況中,尾巴在一個座標軸上要走的方向只跟頭在該軸的方向有關:

  • 若頭在東北邊,則尾巴也要往東北走;
  • 若頭在東南邊,則尾巴也要往東南走;
  • 若頭在西北邊,則尾巴也要往西北走;
  • 若頭在西南邊,則尾巴也要往西南走。

因此南北向和東西向的條件其實可以拆開判斷,變成:

  • 若頭在東側,則記下往東,否則記下往西;
  • 若頭在南側,則記下往南,否則記下往北;
  • 以上兩者合起來即可。

而恰巧的,上面的兩個判斷正好分別是南北不動或東西不動時要做的判斷,而且不動正好是三一律中三個比較關係中剩下的一個 (也就是兩個都不成立),所以就可以把不動的判斷給拆掉,統一直接判斷在座標軸的方向即可。

至於第二部份十段的部份,還好把兩組變數改成 vector<pair<int,int>> 沒花多少時間,所以還差點擠上前百名。(這天的名次是 192/104,第二部份和 100 名只差 9 秒……)

Day 10: Cathode-Ray Tube

組合語言題出現了。雖然只有兩個指令但多了一個東西搞鬼:現在指令執行有 cycle 數了。好在原先寫的組合語言機器可以稍微拗一下把 cycle 計數給加進去——方式是在執行當中呼叫一個 callback 做為 cycle 結束的快照。花兩個 cycle 的就呼叫兩次就好。

然後第二部份就要我們追 CRT 的光束了。還好這部份寫起來並沒有很困難,直接在那個 cycle callback 裡做事,算出現在光束在哪裡然後打出來。速度不是頂快,但至少很滿意地再次利用了這個組合語言機器。

(會寫起來實在是因為 2016 的組合語言題不在少數……說起來我在考慮要不要寫一個二維座標/格子的函式庫了。)

Day 11: Monkey in the Middle

看到這標題我還以為我們要做一些攔截破解的事了的說。

第一部份是個很標準的照題目敘述做事的題目。比較麻煩的只有每隻猴子的函數不同,還有一個是平方,所以稍微花了點功夫:因為要把猴子存在一個 vector 裡,所以函數就只好讓 std::function<> 幫我處理掉 lambda 函數型態不同的問題。有點搞事的是猴子們原來是依序動作的,不是所有人動過一輪之後才收下其他人的東西。

第二部份則突然冒出數學了,因為如果不管它的話平方上去就上天了。(看到 reddit 上好多人都是被 python 的大數給養壞胃口了,硬跑下去才發現怎麼跑了一小時還跑不完 XD) 不過要化簡也不是隨便模,好在因為要保持的操作是整除性,所需要的模數只要所有條件的公倍數就行了——或是注意到所有整除除數都是質數,模它們的乘積。然後只要小心平方不要爆就行了。

後記:也是 reddit 上有人用了點技巧做了計算,如果真要全算細的話會需要算到 10800 左右的位數——對,是位數不是數值。這是個幾百萬個宇宙都不夠它表示的數量級……

Day 12: Hill Climbing Algorithm

嚇人的標題,但題目本身就是很經典的迷宮而已。只是竟然 reddit 上連出三篇文章發問說怎麼走不到……明明題目特別括號提醒說可以往下跳了的說。1

比較值得一提的是第二部份。我不確定這個思考跳躍需要花多少工夫,但我幾乎是在看到說「從任何一個 a 的點開始」就馬上想到那就從山上走下來就好啦。所以我這邊的時間反而是花在要怎麼把程式微調讓它變成從山上走下來,然後才發現這個好像不是固定終點……總之稍微 hack 了一點東西進去就是了。BFS 函式庫果然好用。

Day 13: Distress Signal

去年的 Snailfish number 逆襲了……雖然不完全是就是了。

這次的題目是個標準的複雜資料比較題,不過用 C++ 寫就是要自己寫 parser。嘛本來這種就是我擅長的東西所以還好。比較跟排序則都直接交給 STL 了:比較有 std::lexicographical_comparestd::equal,然後 operator < 寫好之後就能用 std::sortstd::lower_bound 了,所以我的時間其實都是花在確定 parser 有沒有寫對,以及好好的玩了一下 std::variant

話說本日的 FAQ 是規則文字沒有寫到底造成的誤會:

If both values are lists, compare the first value of each list, then the second value, and so on. If the left list runs out of items first, the inputs are in the right order. If the right list runs out of items first, the inputs are not in the right order. If the lists are the same length and no comparison makes a decision about the order, continue checking the next part of the input.

看起來有不少人直接跳過中間的 compare the first value… 這一段,直接跳到後面 If the left list runs out of item first 了,所以誤以為陣列長度判斷優先。個人是覺得應該可以在這之前加一點點敘述說比較有結果就是結果這樣就是了。

Day 14: Regolith Reservoir

這次逆襲的是敘述中也有連結的 2018 Day 17,這次這兩題的相似度就比較高了。題目本身則相對簡單,只要抓對方式模擬就不是什麼大問題;我會說甚至比起過去的那題來得簡單,因為移動規則都寫給你看了。

Day 15: Beacon Exclusion Zone

這次逆襲的是 2018 Day 23。那題的第二部份其實是我今年以前這 350 星裡難得卡超過一天的題目——那時因為卡到不知道要怎麼做跳去做完 2019 之後才在一個星期後回頭解掉它。

今天這題和之前那題其實很像,都是要我們做曼哈頓距離的聯集,但是之前那題比較狠的是它是 3D 曼哈頓距離,所以等距離空間形成的是個正八面體,這讓事情超級難拆的。後來我是把正八面體的四個面方向當做四個維度去做掃描才終於把它解掉。今天這題只有 2D 所以事情還算可以控制。

不過也因為只有 2D 所以我一開始寫的是個有點暴力的做法,直接對所有座標去問第一部份寫完的程式,有找到空格就是答案;想當然爾座標空間範圍四百萬一定慢。後來看到 reddit 上的 visualization 之後才突然想到對耶,我們可以把方形轉正!3D 的正八面體不好轉正所以要用個很怪的四個方向去切,但 2D 的斜方形很簡單啊!所以就用了斜方形座標再加上簡單的掃描就輕鬆搞定了。

Day 16: Proboscidea Volcanium

哇。這題有競賽題中等題的難度喔。要不是我有寫一份 A* 放著這題可能又要加個一兩小時……

雖然還沒仔細看,不過據稱 reddit 上的共識這題就是個很討厭的搜尋題。嘛,至少我是先看到了一個點可以簡化:卡住的閥門的作用就是路過,所以可以先做一次 all-pair shortest path 把圖補完成從一點到另一點最短要幾分鐘,這樣之後就可以不用管卡住的閥門直接跳到需要開的閥門點了。搜尋的東西則是直接走下去,沿路把所有閥門今後的貢獻給求和起來就是了。(例如題目開頭提的 28*13=364 這種,打開 13 的閥門時還有 28 分鐘就直接加 364 上去這樣。)

說起來簡單,但這就代表搜尋要的狀態包括:現在還沒開的閥門、我們的所在、我們還有多久時間。這個結構不化簡一點記憶體就會卡超級多……我在平板上的 termux 就因為這樣被系統踢掉了兩三次。第二部份再加進大象的位置跟時間,於是狀態數又會爆炸性成長 orz

最終的演算法是 A* 啦,但其實一開始就有用 A* 試寫過了,只是效率一直起不來。中間還曾經砍掉重練過一次改成 DFS,想當然爾在第二部份直接卡死給你看,然後再砍掉重練一次寫回 A*,再用上有的沒的各種最佳化才讓它在好一點的時間裡跑過 (附帶兩次 WA)。跑出結果的時間約莫 20 秒,應該算是差強人意吧?再說這個 A* 的用法其實有點歪:一般 A* 是要搜最短距離,但這裡我們要找目標點的最長「花費」(就是總氣壓量啦),所以我是直接把「花費」跟「估計剩餘」都取負號下去跑,當然這裡的估計就要變成最大估計了——這裡有用了一點手腳去求這個最大值不要太扯 (不然 A* 會跑更久),詳細就請看程式碼了。最終第二部份的搜尋節點數計數將近九十萬,和其他人回報百萬級的搜尋量來比應該算普通?

話說這是個星期五題……接下來的週末就是聖誕節前的週末了,看來接下來兩天有場硬仗。

十小時後的補記:在和其他人討論之後有發現了一個類 DP 的遞迴+記憶做法,不過就我寫起來的結果大概也是要 25 秒左右,所以是個另解但應該不會是個快解……

Day 17: Pyroclastic Flow

好喔,玩俄羅斯方塊了。還好我十四年前在批兔上寫的那玩意給我一個概念要怎麼做了。只不過因為我不想用二維陣列在排東西,用的是一列以一個數字的位元表示的一維陣列。然後寫到一半發現落地中跟固定石頭碰撞的函數在左右移動跟掉落時都要用所以抽出來,算是個小插曲吧。

第二部份則是一個有點 tricky 的找迴圈,主要問題是條件沒有那麼簡單。我用的條件是落地時的石頭跟它最後一次受到的方向索引,然後當同樣組合的東西差 D 個石頭重覆出現時記下這個 D,當同樣的 D 出現夠多次就說它是迴圈了。然後就是求出目標的 1T 次對應到迴圈中的什麼地方,查出那時的高度再加上迴圈增加的高度倍數就行了。

Day 18: Boiling Boulders

這是要玩麥塊了嗎 (笑) 總之是個很輕鬆的直接照做題,輸入範圍不大所以就直接開三維陣列記了。第二部份淹水稍微麻煩了那麼一點點不過還算行,只是因為有 0 座標點所以有些面沒淹到……於是就把所有輸入座標全部 +1 了。連我都覺得這 hack 有點微妙,不過能過就行了吧。

結果今年的第三週末兩題都是繁題……反而是 Day 16 這個星期五題有點難度。

Day 19: Not Enough Minerals

可能因為開頭提到的那個 DC 群是某個 Idle game 的 DC 群吧2,這題一出來我的第一反應是現在要來玩 Idle game 了嗎 XD 不過最後終於拿到兩顆星星的時間已經離 Day 20 開放剩三個半小時了。果然我對搜尋剪枝題沒轍。是的,這題又是個策略搜尋剪枝題。這題我甚至還不太確定要怎麼像 Day 16 一樣找 DP 結構……

我的做法的大方向還是 BFS,只是用了一點假設來限定一點搜尋結構;也因為有這個假設的關係,這份答案應該還是假解,只是不確定到底是什麼測資才會戳中問題就是了。先講比較沒問題的條件是,當剩下相同時間時,如果現下狀態 A 的所有八種資源量都沒比已經走過的某個狀態 B 多,那狀態 A 絕對不會獲得超過狀態 B 能獲得的晶洞數量,因此就可以把狀態 A 給剪掉。雖然我有八九成把握這應該對但還沒有好好數學證明一番就是了。

比較有問題的條件則是,假設最佳路線不會拖太晚做晶洞機器人,因此對同一時間的狀態中,我只搜尋晶洞機器人數量和這一時刻的 queue 裡晶洞機器人數目最多的狀態一樣多或少一個的狀態。這個就沒什麼把握了;原本第一部份時這裡甚至沒搜尋少一個晶洞機器人的狀態,但 24 分鐘夠短所以也給過了,不過 32 分鐘的第二部份就在第一筆範例中翻車 (正解 56 但只找到 54),所以就隨便加搜少一個機器人的狀態。搜尋時間暴增但結果給過了所以 (ry

話說回來,我其實有點在想這搜尋時間暴增的原因應該是前一個條件,因為實際測資的第三組中間搜到某個時間有約十二萬筆,每次都線性比過去所以每個 queue 看完是個量大時有麻煩的平方時間,不過一下子也想不到要怎麼去判斷比較快;八維的狀態好像沒什麼好方法可以排個好順序的樣子? (事後補註:這個應該是 Pareto front 問題,但看起來好像本身就是個不好做的問題了。)

Day 20: Grove Positioning System

暴風雨前的寧靜 (?),今天的是相對簡單的模擬題。

這題裡有兩個互相衝突的存取模式:索引存取和隨機插入。std::vector 前者常數後者線性,std::list 前者線性後者常數,所以在不想寫太複雜的資料結構之下 (謎之音:skiplist) 只好就挑一個來用。然後第二部份果然是多次應用,不過因為原本我的第一部份是利用第一次交換時所有元素能夠大致地走下去走得到來寫的,要多次應用這個性質就不能用了,只好再用一個 std::vector 把所有索引的指標給存起來——於是我們有了索引的指標的陣列這個三重星星間接存取的大陣仗 (笑)。這題的「難點」應該就只有在這裡而已了。

——然後我還是因為忘記改數值型態 overflow 而 WA 了。原本這該在答案輸出負數時就要警覺了,但這題輸入有負值所以出負數答案也不怎麼希奇,直到我發現印出的這答案好像不是題目那個大數字的倍數……(白井黑子撞頭.gif)

Day 21: Monkey Math

第 11 天的猴子們逆襲了 (大數字的意味)。好在我在寫時總有種預感好像 int 可能會不太夠用所以提早改成 int64_t 了,所以一切相安無事。

不過一向在細節上嚴謹的 AoC 竟然在這裡漏了一個很大的點:數字不整除的話怎麼辦。我只用整數做運算但也過了,不確定其他輸入是不是也都有這個除法必整除的限制——不然以一個 Day 21 的題目來看這題其實簡單過頭了。

隔天早上的補記:果然這個整除問題在 reddit 上討論超多,不過原因卻是反過來的:由於許多人使用第一部份的做法對第二部份進行二分搜尋,當除法使用整數除法時會造成有多解的狀況,然後就陸陸續續的跑上來問為什麼多解了。不過要能二分搜也是題目輸入條件的關係:整個猴子結構是個樹,以及變數不出現在除法的右分支。這兩個條件合起來表示最後的函數是線性函數,所以二分搜才會對;如果變數出在除法右分支這就變成分子分母至多一次的有理函數,它是 y=1/x 的平移縮放所以二分搜會搜到奇點上去;更不用說如果不是樹 (變數會出現不只一次) 那函數就變成一般多項式了,這就沒有什麼好方法可以解。

Day 22: Monkey Map

看到範例跟輸入檔都是個方塊展開圖就猜到第二部份會要我們摺起來了。嘛所以我決定為了搞自己寫一個通用的「摺」方塊演算法 (實際上就是設法求出哪個邊走出去會走到哪個邊)。這樣竟然也有 400 名算是還不錯?

然後因為本來就想貼上 reddit 的 Megathread 所以用英文寫了下這個「摺」方塊演算法是怎麼做的,放在這裡。後來看到 reddit 上有其他人也是用類似的方式,但他用的動詞 “zip” (拉拉鍊) 我覺得更形象化地表現了這演算法是怎麼作用的。

Day 23: Unstable Diffusion

前面提過的二維座標函式庫登場了。原本寫好的那部份還含有元素存取,不過因為那個存取有邊界檢查不能直接用,只好自己 hack 一個存取 map 元素的 lambda 了。

還有,看到第一部份只跑十回合大概就猜到第二部份會要我們跑到完,不過沒注意到的是第二部份換了問題了 (變成問停下來的回合數而不是第一部份問的面積),所以跑了要花 15 秒的程式兩次…… (不過後來用 -O3 之後只花兩秒就跑完了。該不會是 lambda 沒怎麼 inline 的關係?!)

Day 24: Blizzard Basin

很搞事的迷宮題。搞事的點在於判斷一個點能不能走有點麻煩。我的做法比較反過來,是倒回去推算如果這裡到時會有某方向的暴風雪來,那在一開始這個暴風雪應該在哪,再回頭去檢查那裡的輸入點是不是這個方向的暴風雪。這樣做可以不用每個時間點都去模擬現場暴風雪的所有狀況比較簡單;我總覺得有些人的慢應該是這裡在慢而不是搜尋在慢。另外這次終於用到二維座標函式庫的陣列元素存取的部份了,然後發現有些東西不夠所以回頭補了幾個函數進去。

然後第二部份要我們走過去走回來再走過去。好啦,確實這個一次搜尋不太好做,不過拆成三次搜尋就要看搜尋有沒有寫得很好改了。所以好在我的 BFS 函式庫的狀態 class 早就是超級能自訂的東西,所以五分鐘就改完一次通過。

Day 25: Full of Hot Air

最後一天果然是歡樂題,出的是平衡五進位——跟平衡三進位一樣的概念,就變成 -2 到 2 而已。

然後還是被卡在各種技術問題,例如又讓變數溢位了。不過至少今年在最後的最後第 50 顆星總算是拿到前百名的分數了。

年度心得

好像還是需要來做個簡單的總結。

今年其實我是有一個不算大的限制在:大概上面也有簡單提到了,我今年的環境是在我的三星平板上裝了 termux 再在裡面開發的,然後為了寫 code 順手一點也是裝了窮人版 vscode 伺服端 code-server,再從瀏覽器連進去。主要理由其實是今年我中午時間不會在我平常用的筆電前面,而我這台三星平板有順道買了附硬體鍵盤的保護套可以當小筆電用,所以就把環境都放進這裡來了。最一開始說的把前三年清掉就是順道測試這樣的設定寫起來如何,這兩個月下來看起來以這種小程式來說還滿 OK 的;這陣子應該可以回頭來解 Project Euler 了。

話說回題目。今年果然還是在搜尋題卡住一陣子,畢竟這種題型一直都是我的弱項,有時甚至要想一會兒才會看得出來題目裡面的 DP 結構或其他東西。AC 時間超過一小時的題目依時間長短排列是 Day 19 (如上所述晚到爆炸)、Day 16 (下午發覺搞不定所以晚上回家才寫完)、Day 20 (因為溢位爆炸)、Day 22 (在寫摺方塊)、Day 17 (找迴圈的除錯時間久了一點)。整體來說還是跟我最前面提的一樣,要全通不需要到我這種程度的經驗也能做得到,中間用到的都是最基本的遞迴、搜尋、演算法實作等等而已。(摺方塊大概算是例外,那題後來看討論結果是大家的展開圖形狀是一樣的,所以直接寫死的對照表可以和其他人對看看有沒有錯)

這裡就來放個小附註好了:根據 AoC 網站作者本人表示,在 2022 剛結束左右的時間點 400 星全通的人有約 570 個人

祝大家聖誕節快樂!

註腳

  1. 我在第一篇這樣說的文章下的第一個回覆簡單引用了這個括號,然後將近一天之後已經 +25 了 XD ↩︎
  2. 那個 Idle game 是 Antimatter Dimensions,正好在這題出來的前兩天出了最終更新 Reality Update;不過這題外話就扯太遠所以放註腳了。 ↩︎

Comments

  1. 分享一個 Day 16 狀態壓縮 DP 作法在 Ideone 上 Python (PyPy) 跑 ~2s:https://ideone.com/GCw4ik

    利用了 input 中 flow > 0 的 valve 只有 15 個的特性,DP 裡面其中一個 2^15 的維度直接存哪些開過了。

    另外一個觀察是 Part 2 大象可以建基於 Part 1 人開完的狀態重新出發,不用把 (人, 大象) 的位置一起編碼進狀態裡面。

    (感謝友人 P 跟我討論這題,賽中原本遞迴記憶化做法 Part 2 多了一個 N 被迫用 C++ 重寫 Ruby code…XD)

發佈留言

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

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