SRM 383 500pt: FloorBoards
問題概要
H*W(H,W<10)のボードがあり、各マスは空きか何かが埋まっている。空きのマスだけを長方形の板(幅1、長さは任意)でカバーしたい。ただし板はオーバーラップしてはならない。必要な板の最小枚数を求める問題。
考えたこと
- 何か最大流の匂いがするような。
- でもサイズ小さいし、ビットDP?
- コストの計算に上の列の情報がいるから前の列と今の列で2重にビットを回すのか。
- 計算量がH*2^(2*W)*Wでちょっと多い。2^20 * 100を計算すると10^8。ギリギリ間に合うか?
- 書き始める。気をつけるべきは、余計な工夫をしないこと。バグの温床になるし、最大ケースのテストが面倒になる。
- 向こうで最大ケースが1sちょい。投げたら無事通った。
practiceで通したコード
計算量O(H*2^(2*W)*W)。
#include <vector> #include <string> #include <algorithm> using namespace std; const int MAX_W = 10; const int INF = 1<<29; int dp[MAX_W][1<<MAX_W]; struct FloorBoards { int layout(vector <string> room) { int H = room.size(), W = room[0].size(); //初期化 for(int i=0; i<H; i++){ fill(dp[i], dp[i] + (1<<W), INF); } //ビットDP for(int i=0; i<H; i++){ for(int bit1 = 0; bit1 < (1<<W); bit1++){ //bit1: 上の列の組み合わせ for(int bit2 = 0; bit2 < (1<<W); bit2++){ //bit2: 今の列の組み合わせ int cost = (i==0 ? 0 : dp[i-1][bit1]); for(int j=0; j<W; j++)if(room[i][j] == '.'){ if((bit2>>j)&1){ //横に置く場合 if(j==0 || room[i][j-1] == '#' || ((bit2>>(j-1))&1)==0){ cost++; } } else{ //縦に置く場合 if(i==0 || room[i-1][j] == '#' || ((bit1>>j)&1)==1){ cost++; } } } dp[i][bit2] = min(dp[i][bit2], cost); } } } return *min_element(dp[H-1], dp[H-1] + (1<<W)); } };