Leetcode 第97場比賽回顧
一、背景
之前已經寫了幾場比賽記錄了,如第88場、第101場、第126場、第127場、第128場、第129場比賽。
今天上午我做了第97場比賽,現在記錄一下題解吧。
PS1:前三題用了半個小時就做完了,結果最後一題到時間結束也沒做出來。
PS2:做題時不能分心,做最後一題時線上有個問題要處理,處理後回來思路就比較僵死了。
二、比較含退格的字串
題號:844
題目:Backspace String Compare
地址:https://leetcode.com/contest/weekly-contest-87/problems/backspace-string-compare
原始碼:https://github.com/tiankonguse/leetcode-solutions/blob/master/contest/87/A.cpp
題意:對於字串,裡面會有一個 #
代表退格字元。
給兩個字串,問使用退格字元處理之後的兩個字串是否相等。
思路:分別處理兩個字串,然後判斷即可。
處理字串的時候,可以使用棧來處理。
有時候為了方便,直接使用陣列模擬棧即可。
PS:字串 string
也有陣列的特徵,所以直接使用 string
也可以。
三、陣列中的最長山脈
題號:845
題目:Longest Mountain in Array
地址:https://leetcode.com/contest/weekly-contest-87/problems/longest-mountain-in-array/
原始碼:https://github.com/tiankonguse/leetcode-solutions/blob/master/contest/87/B.cpp
題意:給一個數組,求最長的山峰。
山峰定義:長度至少為 3
的連續陣列,前半部嚴格增序,後半部嚴格降序。
思路:第一種思路就是嚴格按照題意進行迴圈模擬。
此時相當於實現一個狀態機,有三種狀態。
狀態一:只有起點,此時是升序。
狀態二:有起點,有臨時山峰,後面可能升序,也可能降序。
狀態三:降序中,此時可以得到一個臨時答案。
其他邊界情況,都可以歸屬於狀態一起點。
程式碼如下:
其實,這道題還有一種很簡單的思路。
我們先掃描兩邊陣列,分別計算每個點到兩邊降序的最長距離。
然後在掃描一遍陣列,分別計算當前頂點為山峰時的最大距離。
結果取 max
即可。
四、一手順子
題號:846
題目:Hand of Straights
地址:https://leetcode.com/contest/weekly-contest-87/problems/hand-of-straights/
原始碼:https://github.com/tiankonguse/leetcode-solutions/blob/master/contest/87/C.cpp
題意:給一個序列,問是否可以分成多個分組,每個分組的大小是 W
,且是連續升序的陣列組成。
連續升序的含義是相鄰數字之差為一。
思路:既然數字可以隨機選擇,我們直接使用 map
計數儲存即可。
每次從 map
中取最小的數字,然後分別判斷後續連續 w
個數字是否存在,存在則更新計數。
最後 map
為空則代表有答案。
五、訪問所有節點的最短路徑
題號:847
題目:Shortest Path Visiting All Nodes
地址:https://leetcode.com/contest/weekly-contest-87/problems/shortest-path-visiting-all-nodes/
原始碼:https://github.com/tiankonguse/leetcode-solutions/blob/master/contest/87/D.cpp
題意:給一個聯通圖,問從一個頂點開始遍歷所有頂點的最小路徑。
思路:這道題是經典的旅行商問題,當資料量較小的時候,解決方法很多。
我剛開始一看點的個數最多是 12
個,便使用 dfs
暴力搜尋,結果超時了。
然後便想到使用狀態DP來壓縮狀態,從而避免重複計算。
狀態定義: F(start, state)
代表當前起點是 start
且當前地圖是 state
時,遍歷所有頂點需要的最少步數。
地圖 state
是使用 bit
位來儲存每個頂點的狀態,含義為當前頂點是否訪問過。
狀態轉移:每次從當前起點,轉移到相鄰的邊。
正常情況下,我們就可以通過 dfs
加狀態DP解決這類問題了。
但是實際情況時,當圖中有環時,這樣就會導致死迴圈。
也是因為這個原因,最終我沒有過這道題。
後來想到,問題出在狀態轉移上。
一個好的狀態轉移應該保證子狀態是收斂的。
收斂的方法就是確保子問題個數是逐漸減小的。
比如對於地圖 state
,子問題應該是比父問題多覆蓋一個頂點。
這樣遞迴下去就可以保證狀態收斂(每次多覆蓋一個頂點,頂多 N
次)。
這裡我們狀態轉移的時候,不能轉移到相鄰的邊,而應該轉移到尚未訪問的頂點。
所以,這裡還需要預先計算出任何兩個頂點的最短距離,這個使用 flood
演算法即可解決。
解決 dfs
的死迴圈問題後,我開始想是否存在其他方法。
瞬間發現狀態的定義稍微修改一下,就會有一個非常簡單的方法。
狀態定義: F(start, state)
代表空地圖到達當前狀態的最小步數。
狀態轉移:每次向相鄰的邊轉移,判斷狀態是否存在,不存在則儲存狀態,存在則說明已經有最優值。
這不就是典型的 BFS
嗎?
所以這裡使用一個 queue
就可以解決問題了。
其實,這裡還有一個問題:狀態 F(start, state)
怎麼儲存呢?
由於兩個資料都很小,我的方法是把兩個數字使用位運算壓縮到一個數字上,然後使用 map
記錄其最小步數即可。
六、最後
其實這次四道題都不難,但是最後一道題我習慣性的使用 DFS
去搜索,結果就死迴圈了。
直到後來調整狀態轉移公式時, DFS
才正常解決這個問題。
然後想其他方法時,馬上就想到 BFS
。
可見第一印象、第一想法很重要。
一不小心先入為主,就會被帶到坑裡了,再想改變就很難了。
-EOF-