動態規劃 狀態壓縮

2021-08-25 05:37:33 字數 3136 閱讀 7034

這個題目的題意很容易理解,在乙個n*m的格仔裡,我們現在有兩種型別的磚塊,1 * 2和 2 * 1,問一共有多少種方案,可以將整個n*m的空間都填滿。

最簡單的例子就是下面的了: 

程式設計之美中題目:

某年夏天,位於希格瑪大廈四層的微軟亞洲研究院對辦公樓的天井進行了一次大規模的裝修.原來的地板鋪有 n×m 塊正方形瓷磚,這些瓷磚都已經破損老化了,需要予以更新.裝修工人們在前往商店選購新的瓷磚時,發現商店目前只**長方形的瓷磚,現在的一塊長方形瓷磚相當於原來的兩塊正方形瓷磚,工人們拿不定主意該買多少了,讀者朋友們請幫忙分析一下:能否用1×2的瓷磚去覆蓋 n×m 的地板呢?

下面我們來分析:

這個題目類屬於狀態壓縮dp,對於狀態壓縮dp,其實最簡單的理解就是把狀態用位元位的形式表示出來,我們會在下面用例子來說明。

假如現在我們在鋪磚 位置(i,j), 並且假設之前的位置已經鋪設好的了,在這個位置,我們的選擇:

不用鋪磚了,可能在(i-1, j)的時刻已經被豎著鋪上了,然後考慮的是(i, j+1)

橫鋪磚,將(i, j+1)也鋪上了,然後考慮的是(i, j+2)。

豎著鋪磚,(將i,j)和(i+1,j)鋪上乙個豎立的轉頭。

所以我們如下翻譯我們的選擇,在位置(i, j) 如果我們選擇橫著貼磚,那麼將(i, j), (i, j+1)都填寫成1,如果豎著貼磚,我們將(i,j)填寫成0,將(i+1, j)填寫成1. 

註解:用 0 表示該瓷磚的鋪放對下層的鋪放有影響。1表示沒有影響。

難點1:閱讀到此處,讀者或許會疑惑為什麼不直接都用1來表示該區域已經貼了瓷磚,而是用0 和 1 來表示。正如上面註解所述用「 0 表示該瓷磚的鋪放對下層的鋪放有影響。1表示沒有影響。」 那這樣有什麼好處呢?下面問題來解答。

問題1:為什麼要這麼計數呢,我覺得應該這樣理解:

在橫著貼磚的時候,(i, j), (i, j+1) 都是1,這個值其實對下一行如何選擇沒有影響。

豎著貼磚的第二個,我們也選擇了1, 因為這個磚頭結束了,對下一行如何選擇依然沒有影響。

而豎著的第乙個磚頭,這個磚頭是對下面有影響的,如果(i,j)是0,那麼(i+1,j)只有是1的情況下才能滿足條件。

即當設為1表示對下一行沒有任何影響了。

難點1解答:問題1的第三點解答了難點1。而這樣的好處就在於方便下面問題2判斷相容性。

其實我們在上面已經基本給出分析, 如果我們現在鋪設 (i,x) x這裡表示第i行,第x列

如果值 i 行,j 在x位上的值是0, 那麼第 i-1行,j的值在x位上一定是1。因為不可能在同一列相鄰的位置鋪兩個豎著的 第乙個(如果這樣的話,第乙個豎著的瓷磚的下面乙個瓷磚塊就會和第二個豎著的瓷磚的第乙個瓷磚塊重疊),如果滿足下一步測試的是(i, x+1), 否則直接返回不相容。 

如果值 i 行,j在x位置的值是1 .

那麼有可能有兩種情況: 

a. (i-1, x)是0, 這個時候一定是豎著鋪設了,下一步檢測的是(i, x + 1) 

b. (i-1, x) 是1, 如果是這樣的話,那麼(i, x)一定是要選擇橫著鋪了,那麼(i,x+1)也一定是1,並且(i-1,x + 1)一定是1(如果是0,就是豎著鋪了),如果不滿足就返回不相容,滿足條件 就測試(i,x + 2)

對於第一行的相容性,我們要做一下特別的分析,在第一行中,要麼放0, 要麼放1。

加入當前測試的是 dp(0, j)的第 x的位元位,即第0行,x列

如果x是1,那麼 x + 1 也一定是1,然後測試到 x + 2

如果x是0, 那麼直接測試下乙個 x + 1

特別注意:這裡的判斷的(i,x)一定不是由(i,x-1)位橫著鋪磚過來的,否則直接x=x+2,就不會來判斷(i,x)位了。

問題3:為什麼可以使用動態規劃演算法來解決這個問題?

這就得從動態規劃的特性上去找:

(1)最優子結構

用f[i][j]表示第i行j狀態鋪磚塊的方案數,一定等於i-1行所有的能與狀態j相容的狀態k的方案的總和。

(2)重複子問題

求f[i][j]即第i行的每乙個狀態一定要用到第i-1行的各個狀態。

問題4:從狀態壓縮的特點來看,這個演算法適用的題目符合以下的條件:

1.解法需要儲存一定的狀態資料(表示一種狀態的乙個資料值),每個狀態資料通常情況下是可以通過二進位制來表示的。這就要求狀態資料的每個單元只有兩種狀態,比如說棋盤上的格仔,放棋子或者不放,或者是硬幣的正反兩面。這樣用 0 或者 1 來表示狀態資料的每個單元,而整個狀態資料就是乙個一串 0 和 1 組成的二進位制數。

2.解法需要將狀態資料實現為乙個基本資料型別,比如 int, long等等,即所謂的狀態壓縮。狀態壓縮的目的一方面是縮小了資料儲存的空間,另一方面是在狀態對比和狀態整體處理時能夠提高效率。這樣就要求狀態資料中的單元個數不能太大,比如用 int 來表示乙個狀態的時候,狀態的單元個數不能超過 32(32 位的機器)。

/狀態壓縮dp*****填充地板  */

#include #include #include using namespace std;  

#define nmax 1000

#define mmax 1<<5

bool testfirstline(int j, int m) // 主要用來測試第一行的相容性

int allstates = 1 << m;

long long f[nmax][mmax];

int i,j;

memset(f, 0, sizeof(f));

for(j = 0; j < allstates; j++)

} int k;

for(i = 1; i < n; i++)

} }

} cout << f[n-1][allstates-1]<< endl;

return 0;

}

狀態壓縮動態規劃

動態規劃的狀態有時候比較難,不容易表示出來,需要用一些編碼技術,把狀態壓縮的用簡單的方式表示出來。典型方式 當需要表示乙個集合有哪些元素時,往往利用2進製用乙個整數表示。一般有個資料 n 16 或者 n 32 這個很可能就是狀態dp的標誌,因為我們要用乙個int的二進位制來表示這些狀態。要注意好這些...

狀態壓縮動態規劃

我們可以使用乙個01串a來表示乙個集合。對於數x x 0 用ax 0表示它不在該集合中,用ax 1表示它在該集合中。將01串a看作是乙個二進位制數,我們把它轉換為十進位制,就可以使用乙個十進位制整數來表示乙個實際使用二進位制方式表示的集合。這樣,我們可以使用位運算方便地處理集合的操作。交集兩個集合a...

動態規劃之狀態壓縮

題目 是hdu 1074,題目大意是說 有幾門課的作業需要做,已知每門課作業的deadline和完成該作業需要花費的時間,求乙個作業的完成順序,使得最終的超時的天數最小。比如,現在如下輸入 computer 3 3 english 20 1 math 3 2 其中,第一列表示課程名,第二列表示dea...