[CF1201D] Treasure Hunting 題解
題解區有一半是 \(\mathcal O\left(n\log n\right)\) 做法,剩下兩篇 \(\mathcal O\left(n\right)\) 做法,跑得又慢,程式碼還又臭又長,活生生整出大模擬的味道,明明是可以寫的很簡潔的程式碼。
在下莽某人不才,暫時拿下你谷最優解:
正片開始
廢話到此為止,接下來講做法,其實大家題解裡做法都已經講得比較清楚了,我這裡還是再講一遍。
給定一張網格圖以及一些特殊點和特殊列,只允許向左右走或在特殊列向上走,問經過所有特殊點的最短路徑長度。
第一眼就是 dp,感覺就很對,先設一個 dp 狀態 \(f_{i,j}\)
轉移方程就不寫了。
然後發現光狀態就有 \(\mathcal O\left(n^2\right)\) 個,鐵定爆炸,所以考慮做一點優化。
首先肯定從狀態入手,發現要經過全部的特殊點,那麼在一層內移動方式已經固定了,到達這一層後,要麼先走到最左邊的特殊點,再往右邊走到最右邊的特殊點,要麼反過來,中間的點可以忽略不計。
這下簡單了,每層只記最左邊和最右邊的點就可以了,時間複雜度瞬減一個 \(n\)。
大體思路上就是這樣,但是細節還是有的。
首先,如果有空行,那麼所有資訊應該繼承上一行的。
其次,縱向上不一定是走 \(n-1\) 步,頂端如果有空行,注意不用跑到第 \(n\) 行,應當將 \(n\) 設為最靠上的特殊點的縱座標。
然後就是一點小分類討論,比如從 \((1,1)\) 走到 \((2,3)\) ,如果第 \(1\) 列到第 \(3\) 列中有特殊列,那麼橫向移動距離就是 \(3-1=2\),否則的話應該找最近的特殊列去“借道”,比如如果第 \(5\) 列是特殊列,那麼橫向移動距離就是 \(5\times2-3-1=6\),所以要先預處理每一列左右兩邊最靠近的特殊列,這樣就可以做到 \(\mathcal O\left(1\right)\) 查詢,否則如果用二分會變成 \(\mathcal O\left(n\log n\right)\)
最後是程式碼,自認為還挺簡潔的,跑的也不錯。