「動態規劃」筆記(一)
//主要摘抄自參考資料2333
最優性原則&&無後效性 最優子結構
狀態的轉移開銷主要包含兩個方面:每個狀態轉移的狀態數,計算新的狀態的時間.
保證從已經更新的狀態轉移過來 bool?
考慮等效轉化,使決策沒有後效性,如骨牌覆蓋往下放轉化為往上放,往右轉化為往左。同時將狀態變為\((i,j)\)是否覆蓋
考慮優化,橫縱置換?
考慮刪除亢余狀態,同一個階段明顯。。刪除
DP出現兩種要統計的
一個作為狀態的一維,一個作為內容
設兩個獨立的函數——基本不用
狀壓DP【集合】
集合內元素作為狀態,一般在數據規模較小時適用,時間復雜度一般為指數級別。一般二進制壓位表示集合
例題:[GD012014]拯救莫莉斯
? POJ Corn Fields
? POJ 炮兵陣地
插頭DP
與連通性有關或者蘊含連通信息,基於狀壓基礎?
狀態:階段,連通性,插頭
輪廓線
已決策格子和未決策格子的分界線
插頭
表示各自可以在這個方向與外界相連【二進制記錄是否有與這條輪廓線相連的插頭】
連通信息最小表示法
:最左邊格子為標記,給連通塊編序號
Ural 1519 Formula 1
按行DP
影響行轉移的變量:行數、下插頭數決定下一行上插頭、連通性
設計狀態\(f(i,S,C)\),\(S\)代表下插頭狀態,\(C\)代表聯通性
轉移:對每個狀態通過指數時間枚舉下行插頭狀態,再暴力維護可行性和連通性。
從第\(i\)行轉移到第\(i+1\)
==狀態轉移數<=2^n???==
用搜索或並查集計算連通性
逐格DP
發現同行某些列,沒有下插頭時,對下一行不造成影響,是冗余信息,
改進狀態為\(f(i,C)\),只保存下插頭的連通性\(C\)。
同樣把格子的連通性記錄在插頭上\(f(i,j,C)\)表示格子\((i,j)\)的指向它的輪廓線的
插頭的連通狀態
情況1 新建一個連通分量,這種情況出現在\((i, j)\)有右插頭和下插頭.
新建的兩個插頭連通且不與其它插頭連通,
這種情況下需要將這兩個插頭連通分量標號標記成一個未標記過的正數,重新\(O(n)\)掃描保證新的狀態滿足最小表示.
情況2 合並兩個連通分量,這種情況出現在\((i, j)\)有上插頭和左插頭.
如果兩個插頭不連通,那麽將兩個插頭所處的連通分量合並,標記相同的連通塊標號,\(O(n)\)掃描保證最小表示;
如果已經連通,相當於出現了一個回路,這種情況只能出現在最後一個非障礙格子.
情況3 保持原來的連通分量,這種情況出現在\((i, j)\)的上插頭和左插頭恰好有一個,下插頭和右插頭也恰好有一個.下插頭或右插頭相當於是左插頭或上插頭的延續,連通塊標號相同,並且不會影響到其他的插頭的連通塊標號,計算新的狀態的時間為\(O(1)\).
註意當從一行的最後一個格子轉移到下一行的第一個格子的時候,輪廓線需要特殊處理.
值得一提的是,上面三種情況計算新的狀態的時間分別為\(O(n),O(n),O(1)\),
如果使用前面提到的第二種最小表示方法,情況1只需要\(O(1)\),但是情況3可能需要重\(O(n)\)新掃描.
逐行遞推的每一個轉移的狀態總數為指數級,而逐格遞推為\(O(1)\),
每次計算新的狀態的時間兩者最壞情況都為\(O(n)\),逐行遞推自帶常數大【霧
狀態壓縮編碼與解碼
[ 1 ] 直接位運算編碼: 狀態壓縮將\(C\)壓成\((p+1)\)進制數,\(p\)代表可能出現的最多連通塊數,根據題意滿足\(p\leq \lfloor n/2 \rfloor\),且一般取\((p+1)\)為\(2\)的冪次,方便進行位運算【不用進行快速冪】
如需大範圍修改連通塊標號,最好預先將狀態\(O(n)\) 解碼到一個數組中,修改後再\(O(n)\)計算出新的\(p\)進制數;==//存疑==
而對於只需要局部修改幾個標號的情況下,可以直接用\(\lfloor x/p^{i-1} \rfloor mod p\)來獲取第i位的狀態,用\(\pm k \times p^{i-1}\)直接對第\(i\)位進行修改.
[ 2 ] 預編碼:將所有連通性狀態搜索出來,並預先標號為\(0,…,tot\),之後狀態\(f(i,j,k)\)就表示在第\(i\)行第\(j\)列編號為\(k\)的連通性狀態。
[ 3 ] hash編碼: 通過hash將連通性狀態映射至一個較小的空間上,與預編碼一樣都是為了消除直接編碼中的冗余狀態。
預編碼可以在解碼時結合hash table食用。
==記憶化寬度優先搜索==
括號表示法
?因為是哈密頓回路,插頭兩兩匹配,且不交叉。【如果交叉,可得兩個連通塊可以合並為一個,矛盾】
每次轉移相當於輪廓線上當前決策格子的左插頭改成下插頭,上插頭改成右插頭的狀態.
參考資料
【1】陳丹琦《基於連通性狀態壓縮的動態規劃問題》
「動態規劃」筆記(一)