TopCoder SRM 600 Div1 Medium PalindromeMatrix
問題
各成分が0または1の行列Aが与えられる。
Aの行のうちrowCount個以上の行が回文であり、
Aの列のうちcolumnCount個以上の列が回文になるように、
Aの成分をいくつか書き換える。
書き換えなければならない最小の個数はいくつか、求めよ。
制約条件
Aの行、列は14以下の偶数。
方針
回文にする行は、14C7 = 3400くらいしかないので全通り試す。
以下行を固定して考える。
列をペアごとに考える。左から1行目と右から1行目、左から2行目と右から2行目……みたいに。
ペアじゃない列は独立とみてよい。
ペアの列ごとに、(回文にすると決めた行とつじつまを合わせつつ)
どちらの列も回文にしない場合のコスト
どちらかの列を回文にする場合のコスト
両方の列を回文にする場合のコスト
はunion-findを使えば簡単にO(nm)くらいの計算量で計算できる。
そしたら、0, 1, 2個回文にする場合のコストがそれぞれ求まるので、
dp[回文を作った個数] := それにかかるコスト
というテーブルを全てのペアごとに更新していくDPをすれば、
全体でcolumnCount個の列の回文を作るコストが求まる。
全体の計算量はO(2^n * nm)くらい。
ソースコード
int h, W; vector<string> A; int dp[2][1000], p[1000], zero[1000], one[1000]; bool use[1000]; int root(int x){ if(p[x] == x) return x; return p[x] = root(p[x]); } void merge(int a, int b){ a = root(a); b = root(b); if(a == b) return; p[b] = a; zero[a] += zero[b]; one[a] += one[b]; } //pos列目とW-1-pos列目のペアだけを考える //rowであらわされるbitの行と、colであらわされるbitの列はぞれぞれ回文にする int calc(int row, int col, int pos){ rep(i, h * W){ p[i] = i; zero[i] = A[i / W][i % W] == '0'; one[i] = A[i / W][i % W] == '1'; use[i] = 0; } rep(i, h) if(row >> i & 1){ rep(j, W / 2) if(j == pos) merge(i * W + j, i * W + (W - 1 - j)); } rep(j, W) if(col >> j & 1){ rep(i, h / 2) merge(i * W + j, (h - 1 - i) * W + j); } int res = 0; rep(i, h * W) if(!use[root(i)]){ use[root(i)] = 1; res += min(zero[root(i)], one[root(i)]); } return res; } vi solve(int bit){ vi w; rep(i, W / 2){ int l = 1 << i, r = 1 << (W - i - 1); w.pb(calc(bit, l | r, i)); w.pb(min(calc(bit, l, i), calc(bit, r, i))); w.pb(calc(bit, 0, i)); } return w; } class PalindromeMatrix { public: int minChange(vector <string> A, int rowCount, int columnCount) { ::A = A; h = A.size(); W = A[0].size(); int ans = inf; rep(bit, 1 << h){ if(__builtin_popcount(bit) != rowCount) continue; vi w = solve(bit); int cur = 0, next = 1; rep(j, 1000) dp[0][j] = inf; dp[0][0] = 0; rep(j, w.size()){ rep(k, 1000) dp[next][k] = inf; rep(k, 998){ dp[next][k + 2] = min(dp[next][k + 2], dp[cur][k] + w[j]); dp[next][k + 1] = min(dp[next][k + 1], dp[cur][k] + w[j + 1]); dp[next][k + 0] = min(dp[next][k + 0], dp[cur][k] + w[j + 2]); } swap(cur, next); j += 2; } rep(j, 1000) if(j >= columnCount) ans = min(ans, dp[cur][j]); } return ans; } };