という制約がいかにも怪しい!
問題概要
1 以上 20 以下の整数からなる、長さ の数列 が与えられる。
この数列の部分列 (連続でなくてよい) であって、任意の整数について、その部分列に含まれる個数が 0 個または 2 個であるものを考える。
そのような部分列に含まれる要素の個数の最大値を求めよ。
制約
考えたこと
「部分列」とあるので、最初は部分列 DP をするのかと考えた。が、良い感じの計算量になる方法は浮かばなかった。というのも「部分列 DP を更新する際に、どの値は既に使ったのか」を管理したいのだ。bit DP ができるなら、したい。
そこで、代わりに次の DP を考えることにした。
dp[S]
: から、「整数値集合 に含まれる整数がちょうど 2 個ずつ含まれる」ような部分列をとれるような の最小値(とれない場合は )
この DP を更新するために、以下のデータをあらかじめ用意しておく。これは、 として、 の計算量で求められる。
nex[i][c]
: となるような () の最小値
また、DP の更新に要する計算量は となる。以上をまとめて、計算量は と評価できる。
コード
#include <bits/stdc++.h> using namespace std; int main() { const int MAX = 20; int N; cin >> N; vector<int> A(N); for (int i = 0; i < N; i++) cin >> A[i], A[i]--; // nexind[i][c] := 各文字 c について A[j] = c なる j (>= i) の最小値 vector nexind(N+1, vector(MAX, N)); for (int i = N-1; i >= 0; i--) { for (int j = 0; j < MAX; j++) { nexind[i][j] = nexind[i+1][j]; } nexind[i][A[i]] = i; } // bitDP int res = 0; const int INF = 1 << 29; vector<int> dp(1 << MAX, INF); dp[0] = 0; for (int bit = 0; bit < (1 << MAX); bit++) { if (dp[bit] == INF) continue; res = max(res, __builtin_popcount(bit) * 2); for (int v = 0; v < MAX; v++) { if (bit & (1 << v)) continue; int bit2 = bit | (1 << v); int nex = nexind[dp[bit]][v]; if (nex >= N) continue; nex = nexind[nex+1][v]; if (nex >= N) continue; dp[bit2] = min(dp[bit2], nex+1); } } cout << res << endl; }