AOJ 2449 Connect
問題
n個の単語をそれぞれ一行に1つずつ書く。
幅wになるように、自由にスペースを入れることができる。
スペースを入れた後で、上下左右に同じ文字が隣り合っているとき(スペースを挟んではいけない)、1点を得るものとする。
得られる得点の最大値を求めよ。
制約条件
n≦128, w≦16
方針
よくある1行ずつ覚えるbitDP
bitのうち最初のj個は今の行で、残りのw - j個は上の行の状態を覚える。
dp[i][j][bit]を、i行j列までで、今の状態がbitであるときの得点の最大値とする。
これを、j番目に文字を入れるかスペースを入れるかで更新するDPをすればいい。
配列は使いまわせる。
ソースコード
int n, w; int dp[2][1 << 16], bc[1 << 16]; string in[130]; int main(){ cin >> n >> w; rep(i, n) cin >> in[i]; rep(i, 1 << w) bc[i] = __builtin_popcount(i); memset(dp, -1, sizeof(dp)); dp[0][0] = 0; int cur = 0, next = 1; rep(i, n) rep(j, w){ memset(dp[next], -1, sizeof(dp[next])); rep(bit, 1 << w) if(dp[cur][bit] >= 0){ //i行の単語の、今何文字目かは、j以下のbitを見ればわかる int idx = bc[bit & (1 << j) - 1]; if(idx < in[i].size()){ int nxt = dp[cur][bit]; //一つ前の文字と一致するか if(j && (bit & 1 << (j - 1)) && in[i][idx - 1] == in[i][idx]) nxt++; //一つ上の文字と一致するか。 //一つ上の文字が何であるかは、ビットのjより大きい部分を見ればわかる if(i && (bit & 1 << j) && in[i][idx] == in[i - 1][in[i - 1].size() - bc[bit >> j]]) nxt++; dp[next][bit | 1 << j] = max(dp[next][bit | 1 << j], nxt); } if(idx + w - j > in[i].size()){ dp[next][bit & ~(1 << j)] = max(dp[next][bit & ~(1 << j)], dp[cur][bit]); } } swap(cur, next); } cout << 2 * *max_element(dp[cur], dp[cur] + (1 << w)) << endl; return 0; }