面白かった
問題概要
個の整数 が与えられる。この中から最大個数の整数を選んで、それを小さい順に並べたときに等差数列となるようにせよ。
制約
考えたこと
すごく DP っぽい雰囲気の問題。イメージとしては
- dp[ i ] := i 番目の要素が最後になるような最長等差数列の長さ
という感じの DP になりそう。ただこれだけでは公差がわからないので
- dp[ i ][ diff ] := i 番目の要素が最後になるような公差が diff の最長等差数列の長さ
という感じにしたい。これでもまだ diff が 109 くらいになってしまうのでダメで
- dp[ i ][ j ] := i 番目の要素が最後になるような公差が A[ i ] - A[ j ] となる最長等差数列の長さ
としてあげると良さそう。これは言い換えると、最後の 2 要素が A[ j ] と A[ i ] ということになる。このとき、
- A[ i ] - A[ k ] = A[ j ] - A[ i ]
となるような k がもしあったならば、
- dp[ i ][ j ] = dp[ k ][ i ] + 1
という風になる。そのような k がなかったら dp[ i ][ j ] = 2 になる。i とか j とか k の順番が混乱しそうだったのでメモ化再帰で書いてみた。計算量は本来は なんだろうけど、僕の実装だと になってしまってそうだけど、解説を見ると、しゃくとり法に近い要領で でもできそう。
#include <iostream> #include <vector> #include <map> #include <algorithm> using namespace std; vector<int> a; map<int,int> ma; int dp[5100][5100] = {0}; int rec(int i, int j) { if (dp[i][j] != 0) return dp[i][j]; int val = a[i] * 2 - a[j]; if (!ma.count(val)) return 2; int k = ma[val]; int res = rec(k, i) + 1; return dp[i][j] = res; } int main() { int N; cin >> N; a.resize(N); for (int i = 0; i < N; ++i) cin >> a[i]; sort(a.begin(), a.end()); for (int i = 0; i < N; ++i) ma[a[i]] = i; int res = 2; for (int i = 0; i < N; ++i) { for (int j = i + 1; j < N; ++j) { res = max(res, rec(i, j)); } } cout << res << endl; }