1. 程式人生 > 其它 >2934. 插頭DP

2934. 插頭DP

給你一個 \(n \times m\) 的棋盤,有的格子是障礙,問共有多少條迴路滿足經過每個非障礙格子恰好一次。

如圖,\(n=m=4\)\((1,1),(1,2)\) 是障礙,共有 \(2\) 條滿足要求的迴路。

輸入格式

第一行包含兩個整數 \(n,m\)

接下來 \(n\) 行,每行包含一個長度為 \(m\) 的字串,字串中只包含 *.,其中 * 表示障礙格子,. 表示非障礙格子。

輸出格式

輸出一個整數,表示滿足條件的迴路數量。

資料範圍

\(2 \le n,m \le 12\)

輸入樣例:

4 4
**..
....
....
....

輸出樣例:

2

解題思路

插頭dp

插頭dp 主要用來解決一些網格圖上的哈密頓迴路方案的相關問題
本題即 插頭dp 模板題,即給出一個網格圖,並且要求一些格子不能經過,求哈密頓迴路的方案數
做法通常是一格一格做,由於是哈密頓迴路,即每個格子會有兩條出邊(即插頭),處理當前格子的時候之前,記錄上一行邊界出邊的狀態,通常記錄這些的狀態有兩種方法:

  1. 最小表示法:由於上面每一個出邊,一定會對應另外一條出邊,對於每一對匹配邊,用同一個數字表示,不同對匹配邊之間用不同的數字表示,最後所有的這樣的數字連起來的最小表示法即為該邊界的狀態
  2. 括號表示法(效率一般更高,因為可以轉化為進位制之間的運算):從左往右,如果沒有當前邊界沒有出邊則用 \(0\)
    表示,否則如果該匹配邊在左邊用 \(1\) 表示,在右邊用 \(2\) 表示,最後會形成 \(012\) 這樣的數字,但是這樣的數字得用四進製表示,原因在於四進位制好進行位運算

狀態表示:\(f[i][j][s]\) 表示處理到 \((i,j)\) 這個格子時邊界狀態為 \(s\) 時得方案數

狀態計算:
插頭dp 的狀態轉移一般比較複雜,需要分好多情況討論(這裡採用括號表示法):

處理到 \((i,j)\) 這個格子的時候,其左邊界插頭的狀態為 \(x\),上邊界插頭的狀態為 \(y\)\(0\leq x,y\leq 2\)

  1. 如果 \((i,j)\) 是障礙物,則要求 \((i,j)\)
    上不能出現插頭才是合法狀態,即 \(x=y=0\),此時狀態不變
  2. 如果 \(x=y=0\),即左邊界和上邊界都沒有插頭,因為哈密頓迴路要求每一個非障礙物的格子都要有兩個插頭,且這兩個插頭是連通的,即一定有一個出去然後另外一個回來,更新狀態,由定義:\(x\leftarrow 1,y\leftarrow 2\)
  3. \(x=0,y\neq 0\),此時 \(y\) 這個插頭有兩種選擇:向下、向右。向右時當前格子的右邊界狀態即為 \(y\),下邊界狀態為 \(0\);向下時下邊界狀態為 \(y\),右邊界狀態為 \(0\)
  4. \(x\neq 0,y=0\),同樣地,此時 \(x\) 這個插頭有兩種選擇:向下、向右。向右時當前格子的有邊界狀態為 \(x\),下邊界狀態為 \(0\);向下時下邊界狀態為 \(x\),右邊界狀態為 \(0\)
  5. \(x=y=1\),此時兩個插頭連通,當前格子的右、下邊界狀態都為 \(0\),且右邊對應的狀態需要修改,即此時兩個狀態 \(x,y\) 都有一個匹配插頭,且都在右邊,狀態都為 \(2\),由於這兩個插頭已經連通,所以右邊之前未匹配的插頭開始匹配,即將右邊兩個匹配的插頭的第一個插頭狀態修改為 \(1\)
  6. \(x=y=2\),同理,此時兩個插頭連通,當前格子的右、下邊界狀態都為 \(0\),且左邊對應的狀態需要修改,即此時兩個狀態 \(x,y\) 都有一個匹配插頭,且都在左邊,狀態都為 \(1\),由於這兩個插頭已經連通,所以左邊之前未匹配的插頭開始匹配,即將右邊兩個匹配的插頭的第二個插頭狀態修改為 \(2\)
  7. \(x=2,y=1\),此時兩個插頭連通,當前格子的右、下邊界狀態都為 \(0\),由於此時兩個插頭連通,但是對應的匹配的插頭正好左邊的匹配插頭狀態為 \(1\),右邊的匹配插頭狀態為 \(2\),所以這時這樣的狀態不用修改
  8. \(x=1,y=2\),此時兩個插頭連通,但是對於當前 \(x\) 來說,其對應的匹配插頭在右邊,對於 \(y\) 來說,其對應的匹配插頭在左邊,這樣兩條路徑要麼相交,要麼重合,由於兩條路徑相交的話就不再是一條哈密頓迴路,所以只能重合,而重合說明當前遍歷的格子一定得是最後一個合法的格子

設有效狀態數量為 \(S\),則:

  • 時間複雜度:\(O(n^3\times S)\)

程式碼