結果完整**
就是將乙個n * m
的二維矩陣,分成若干個1 * 2
的方格,有多少種分配方式(完全分配)
可以對放置的方式進行模擬,先放置橫著的1* 2
方格,再放置豎著的2 * 1
方格。那麼擺放的小方格方案數
等價於橫著擺放的小方格方案數
,因為當橫著合法
擺放的方格確定後,豎著擺放的方式就已經確定了,直接內嵌。
他的資料範圍為1~11
,暗示可以使用二進位制來進行操作,狀態壓縮。那麼我們選擇對每一列的狀態用乙個二進位制進行表示,當當前行所在的bit
為0時,表示該位置沒有放置;bit
為1時,表示當前位置已經進行了放置。
列號狀態表示
00001
11001
21010
30010
所使用的變數
所以,我們在判斷橫著擺放方格的時候,就需要提前判斷合法的擺放方式,防止豎著的方格放完後,不能完全鋪滿整個矩陣。
那麼預處理的方法就是,對於每一種狀態(二進位制),然後依次遍歷每一行,在遍歷的途中需要記錄的就是連續的沒有放置的方格數(連續的0),當出現連續的0位奇數個時,表示該列的這種狀態是不合法的。
在動態規劃中,我們從每一列開始遍歷,然後從0 ~ 2^n
依次遍歷每乙個狀態。對於每乙個j狀態,我們需要找到乙個他可以和哪個狀態進行轉換。
那麼對於當前列在遍歷時的狀態,我們需要知道這個狀態可以在前面的哪乙個狀態的情況後插入。如果前面當前行i-1
是1,表示現在這一行i
是1*2
方格的後半段,所以對於當前狀態是不能插入的。
在判斷當前狀態是否合法時,可以使用&
來操作,如果結果為0,表示沒有衝突。
除此之外,之前還做了一次預處理,判斷當前狀態是否合法。那麼這個合法的狀態就需要使用|
來進行操作。
這樣會出現乙個小問題,就是這樣問題的時間複雜度為o( 11 * 2^n * 2^n )
,那麼對於最壞的情況下,結果為11 * 2^11 * 2^11
近似於5 * 10^7
,這是乙個臨界點,在超時的邊緣徘徊。
((i & j) == 0 && st[i | j]
這兩個條件換個位置後,就會超時)
所以我們可以將找狀態集的過程提取出來,減少在三重迴圈中的判斷次數。
將結果儲存在乙個vector
的可變陣列中
棋盤一共有0 ~ m-1列
也就是m列
不放小方格,前m-1列
已經完全擺放好並且不伸出來的狀態
// 1-11 列,2^11 個狀態
ll f[n]
[m];
// f[i][j] 從第i-1列伸出,到i列,狀態為j, j 的二進位制位為1表示當前行放置方格,為0表示不放置
vector<
int> state[m]
;// 每一種狀態,對應可以放置的狀態
bool st[m]
;// 是否可以成功轉移,j 狀態放置 k狀態後 是否合法
int n, m;
intmain()
cnt =0;
}else}if
(cnt &
1) is_valid =
false
;// 餘下0位奇數個,不合法
st[i]
= is_valid;
}for
(int i =
0; i
<< n)
; i ++
)memset
(f,0x00
,sizeof
(f))
; f[0]
[0]=
1;for(
int i =
1; i <= m; i ++
)for
(int j =
0; j
<< n)
; j++
)for
(auto
& k : state[j]
) f[i]
[j]+
= f[i -1]
[k];
cout << f[m][0
]<< endl;
}return0;
}
AcWing 291 蒙德里安的夢想
題意 給出 n 11,m 11 的矩陣,要求將矩陣全部恰好分成 1 times 2 或者 2 times 1 的小矩陣,問方案數。題解 主要是dp陣列的含義定義要了解,設 dp 表示的是第 i 行 j 形狀的方案數,這裡的 j 代表的是二進位制的狀態。想象乙個 01 串,然後在串中第 x 個0代表的...
Acwing 291 蒙德里安的夢想
求把 n m 的棋盤分割成若干個 1 2 的的長方形,有多少種方案。1 n,m 11 用f i s 表示第i行狀態為s的方案數 對於s 0表示i 1列無伸向第i列的方格,1表示有伸向第i列的方格 先預處理可用狀態 對這一題所有狀態都可用 state.clear for int i 0 i 1 找可轉...
蒙德里安的夢想
求把n m的棋盤分割成若干個1 2的的長方形,有多少種方案。例如當n 2,m 4時,共有5種方案。當n 2,m 3時,共有3種方案。如下圖所示 輸入格式 輸入包含多組測試用例。每組測試用例佔一行,包含兩個整數n和m。當輸入用例n 0,m 0時,表示輸入終止,且該用例無需處理。輸出格式 每個測試用例輸...