【Codewars】7×7 摩天大樓
介紹
鏈接:7×7 Skyscrapers
C#答案(原因:懶,但是完全可以轉成C++):bajdcc/learnstl
題目(機翻):
在7乘7格的網格中,你只想在每個廣場上放置一個摩天大樓,只有一些線索:
- 摩天大樓的高度在1到7之間
- 一行或一列中的所有摩天大樓樓層數量各不相同
- 你只知道從外面的一行或一列中看到的摩天大樓的數量
- 高層摩天大樓阻擋了後面的下層摩天大樓,導致你看不到後面的大樓了
你能寫一個能解決這個難題的程序嗎?
舉個例子,6x6的:
6x6 摩天大樓 例
(有人說看不懂規則3和4,我就以圖來說明一下)
我根據上圖制作了一個假城市(bajdcc/UnityTest),如下:
虛擬城市
我們看線索中的左側(3,4,4):
規則3和4的說明
要求
輸入:int[]
輸入的數組下標
輸出:int[][]
分析
花了兩天思考這個系列的問題,最終當然是寫出來了(光想沒用)。
這個題目感覺跟數獨很像,我最先想的思路就是一步步推理,直到解決問題,然而,4x4大小的推理規則在6x6大小的問題中,可能需要補充一些東西或有些東西不實用了,這就麻煩了。
老實說,做數獨題的時候,我不怎麽用窮舉法,我是對當前局面進行推理,利用排除法一步步確定答案,但用代碼實現推理就變得困難了,雖然這樣做是最優的(因為根據不做多余計算)。
推理不能用,那就老老實實用窮舉法吧,雖然窮舉法有點low,但只要能過就行。
題目中透露了一些可供優化的信息:各行各列中數是唯一的。這就意味著復雜度從O(n^n)下降到O(n!)。這跟八皇後有點像,但又有所不同,因此我就用回溯法做。
進一步思考
確定用回溯做,也感覺可以借鑒八皇後的思路,還不能馬上開工,有幾個問題:
- 全排列的生成,這個嘛拿之前的代碼粘貼下
- 回溯遍歷的順序,這個順序大有文章
先解決全排列問題:抄了下https://github.com/bajdcc/jProlog/blob/master/src/com/bajdcc/rt/gen/array/RtNorepArray.java#L43 哈哈。
回溯的順序應該是怎樣呢?自己試嘍。
先生成全排列,然後計算從左邊向右看到的樓層數(假設為Sky函數,Sky:=int->int),將結果存到字典中。
會發現,當n=7時,sky(7)=1,可能性最低,都算出來後,排序。
sky(7)<sky(6)<sky(5)<sky(1)<sky(4)<sky(3)<sky(2)
展開順序的話,我肯定先找n=7的情況的,因為這時的不確定性最低(解只有一個),相當於可以確定一個格子的值了。
目前的思路是:找到值=7的數,就直接敲定一排解(1~7);再找值=6的數,這時答案不唯一,回溯;繼續找……
這樣運行程序的話,肯定是耗時很長的,原因是什麽?
再想一想
我們優化程序的目標是回溯時盡可以減少回溯次數,那就必須在決定一個展開順序:先展開不確定性小的,再展開不確定性較大的。
有一種情況,可以減少不確定性:如果一排格子的兩側都有數字(都不是零),那其實可以將它們放到一起回溯。例: 5 | x x x x x x x | 2,這樣可以極大降低可能性。事實證明這樣做有一點效果。
思路有了:
- 將規則排好序(數大,對位匹配優先),回溯規則
- 保存現場,查看是否沖突,一是當前的測試值是否與該行/列沖突,二是測試值是否滿足各行/列數唯一的原則
- 如果失敗,回溯
- 如果成功,level+=1,再測試下一個規則
- 如果level==規則總數,意味著通過了所有規則,此時還不能確定解是否正確
- 規則有覆蓋不到的行/列,此時要將值是零的格子進行填空
- 填空的辦法是用排除法,然後不斷遍歷填空,如果最後填空失敗,則解不正確,如果填空成功,則解正確
註意點:
- 坐標系問題,方向問題
- 判斷條件是否完備
- 盡量用數組做(用list<>竟然比int[]快。。)
- 等等細節問題
代碼比較啰嗦,460行,不貼了(別人寫得都很短簡直了)。
由https://zhuanlan.zhihu.com/p/30713476備份。
【Codewars】7×7 摩天大樓