演算法
考慮 \(\rm{dp}\)
其實誰都知道是 \(\rm{dp}\) , 但是推不出來啊
這個問題的關鍵點在於注意到每次往回走, 必定需要走到之前只訪問過一次的位置, 這樣演算法才有正確性
容易的, 令 \(f_i\) 表示遊覽結束前 \(i\) 個點的最小時間花費, 由上面的結論可知, 對於 \(f_i\) 往回走的更新, 一定可以用 \(f_j, j < i\) 來表示, 因為是最小的花費, 顯然 \(f_i\) 時刻在 \(i\) 點的位置
考慮轉移
注意到僅僅有這樣的情況:
先往前走到 \(i\) ( 再往後走是無意義的, 一定不優 ) , 然後在回頭走, 最後再繞回 \(i\)
這樣有一個問題在於有可能繞回來的過程中依然沒有到達時間限制 \(T\) , 於是必須要等到 \(T\) , 此時路過的點一定能夠全部訪問
在這裡我有一個誤區, 就是同時考慮了往前走在走回來這一情況, 事實上考慮一種即可覆蓋
有轉移柿子
顯然的, 有 \(f_1 = \max(a_1, T)\) , 答案即為 \(f_n\)
但是這樣做是 \(\mathcal{O} (n ^ 2)\) 的, 我們考慮最佳化
資料結構最佳化
不太顯然的, 將轉移柿子中的 \(a_i - a_j\) 提出
有,
直接丟進堆裡維護即可, 複雜度 \(\mathcal{O} (n \log n)\)
利用單調性最佳化
需要一些注意力, 我們觀察到 \(i\) 一定時, \(2 \times (a_i - a_{j + 1})\) 和 \(f_j\) 是單調遞減的, 同時, 注意到當 \(i\) 增加時, 在之前 \(2 \times (a_i - a_{j + 1}) > T\) , 對於更大的 \(i ^ {\prime}\) 顯然也有 \(2 \times (a_{i ^ {\prime}} - a_{j + 1}) > T\)
那麼我們就可以想到一種做法, 對於每一次更新 \(a_i\) , 我們就在單調佇列中把所有滿足 \(2 \times (a_i - a_{j + 1}) > T\) 的 \(j + 1\) 彈出, 其中 單調佇列按照 \(a_i\) 從小到大排序
彈出點的時候, 記錄其最大值 \(k\) , 然後對於佇列中的點, 有
$ f_i = a_n + f_j + T $ , 對於彈出的點, 有 \(f_i = a_n + f_j + 2 \times (a_i - k)\)
每個點進隊出隊一次, 時間複雜度 \(\mathcal{O} (n)\)
事實上因為 \(a_i\) 單調性, 完全可以使用佇列處理
程式碼
略了, 好累啊
總結
有啥好說的, 練 \(\rm{dp}\)
注意考慮轉移時不需要考慮本質相同的轉移
寫部落格就該這樣, 不能看著題解抄, 自己理一遍