狀態壓縮動態規劃,就是咱們俗稱的狀壓DP,是利用計算機二進制的性質來描述狀態的一種DP方式。 算法
不少棋盤問題都運用到了狀壓,同時,狀壓也很常常和BFS及DP連用。 spa
狀壓dp其實就是將狀態壓縮成2進制來保存 其特徵就是看起來有點像搜索,每一個格子的狀態只有1或0 ,是另外一類很是典型的動態規劃blog
舉個例子:有一個大小爲n*n的農田,咱們能夠在任意處種田,如今來描述一下某一行的某種狀態: 搜索
設n = 9;遍歷
有二進制數 100011011(九位),每一位表示該農田是否被佔用,1表示用了,0表示沒用,這樣一種狀態就被咱們表示出來了:見下表二進制
因此咱們最多隻須要 2^(n + 1) - 1的十進制數就好(二進制形式是n個1) 如今咱們有了表示狀態的方法,但內心也會有些不安:上面用十進制表示二進制的數,枚舉了所有的狀態,DP起來複雜度豈不是很大?沒錯,狀壓實際上是一種很暴力的算法,由於他須要遍歷每一個狀態,因此將會出現2^n的狀況數量,不過這並不表明這種方法不適用:一些題目能夠依照題意,排除不合法的方案,使一行的總方案數大大減小從而減小枚舉方法
爲了更好的理解狀壓dp,首先介紹位運算相關的知識。 im
1.判斷一個數字x二進制下第i位是否是等於1。(最低第1位)數據
方法:if(((1<<(i−1))&x)>0) 將1左移i-1位,至關於製造了一個只有第i位 上是1,其餘位上都是0的二進制數。而後與x作與運算,若是結果>0, 說明x第i位上是1,反之則是0。 img
2.將一個數字x二進制下第i位更改爲1。
方法:x=x|(1<<(i−1)) 證實方法與1相似。
3.將一個數字x二進制下第i位更改爲0。
方法:x=x&~(1<<(i−1))
4.把一個數字二進制下最靠右的第一個1去掉。
方法:x=x&(x−1)
【例題1】騎士(P1896 [SCOI2005]互不侵犯)
題目描述
在 n×n(1<=n<=10) 的棋盤上放 k(0<=k<n×n)個國王,國王可攻擊相鄰的 8 個格子,求使它們沒法互相攻擊的方案總數。
輸入格式
輸入有多組方案,每組數據只有一行,包含兩個整數 n 和 k。
輸出格式
每組數據一行爲方案總數,若不可以放置則輸出 0。
輸入樣例
3 2
4 4
樣例輸出
16
79
實際狀壓dp顧名思義,就是採用位運算,來記錄更多的必須記錄的狀態來作dp有了比較深的dp功底後只要對位運算有了解就能夠解決問題。。。
考慮到每行每列之間都有互相的約束關係。所以,咱們能夠用行和列做爲另外一個狀態的部分。用一個新的方法表示行和列的狀態:數字。考慮任何一個十進制數均可以轉化成一個二進制數,而一行的狀態就能夠表示成這樣——例如:1010(2)
就表示:這一行的第一個格子沒有國王,第二個格子放了國王,第三個格子沒有放國王,第四個格子放了國王。而這個二進制下的數就能夠轉化成十進制: 10(10)
因而,咱們的三個狀態就有了:第幾行(用i表示)、此行放什麼狀態(用j表示)、包括這一行已經使用了的國王數(用s表示)。
考慮狀態轉移方程。咱們預先處理出每個狀態(s[x])其中包含二進制下1的個數,及此狀態下這一行放的國王個數(num[x]),因而就有:
f[i][j][s]=sum(f[i−1][k][s−num[j]]),f[i][j][s]就表示在只考慮前i行時,在前i行(包括第i行)有且僅有s個國王,且第i行國王的狀況是編號爲j的狀態時狀況的總數。而k就表明第i-1行的國王狀況的狀態編號
【例題2】牧場的安排(P1879 [USACO06NOV]玉米田Corn Fields)
Farmer John 新買了一塊長方形的牧場,這塊牧場被劃分紅 M列 N 行 (1≤M≤12;1≤N≤12),每一格都是一塊正方形的土地。FJ 打算在牧場上的某幾格土地裏種上美味的草,供他的奶牛們享用。遺憾的是,有些土地至關的貧瘠,不能用來放牧。而且,奶牛們喜歡獨佔一塊草地,因而 FJ 不會選擇兩塊相鄰的土地,即:沒有哪兩塊草地有公共邊。固然,FJ 尚未決定在哪些土地上種草。 做爲一個好奇的農場主,FJ 想知道,若是不考慮草地的總塊數,那麼,一共有多少種種植方案可供他選擇。固然,把新的牧場荒廢,不在任何土地上種草,也算一種方案。請你幫 FJ 算一下這個總方案數。
輸入格式
第 1行:兩個正整數 M 和 N,用空格隔開;第 2到 M+1行:每行包含 N 個用空格隔開的整數,描述了每塊土地的狀態。輸入的第 i+1行描述了第 i行的土地。全部整數均爲 0 或 1,1 表示這塊土地足夠肥沃,0 則表示這塊地上不適合種草。
輸出格式
第 1 行:輸出一個整數,即牧場分配總方案數除以 10^8的餘數。
樣例輸入
2 3
1 1 1
0 1 0
樣例輸出
9
題目大意
給N*M的棋盤,每一個格子不是0就是1,1表明能夠種草,不然不能。相鄰兩個格子不能同時種草,求種草的方案總數。
思路
狀態壓縮類動態規劃,狀壓dp通常會有明顯的數據範圍特徵,即n,m通常都在20之內。可將每一排的N個當作一個N位二進制,先預處理出每一行能夠運行的狀態,這樣能夠去掉不少無效狀態(如110),而後DP處理,枚舉當前有效狀態和上一行有效狀態的關係。
f[i][j] 表示第i行在狀態j的時候的方案數,其中j咱們用一個二進制數來表示。
轉移的時候只要判斷與當前行和上一行是否衝突便可,若是不衝突,分f[i][j]=∑f[i−1][k]其中k爲不衝突的狀態。Ans=∑1≤i≤numf[n][i] 就是最後的答案(num爲狀態總數)。
初始條件:f[1][i]=1 (1<=i<=a[1].num).