---
tags: VNOJ, DP, Time Optimized, Fibonacci, SPyofgame
---
Dãy số dài nhất
===
[https://oj.vnoi.info/problem/skfib](https://oj.vnoi.info/problem/skfib)
-----
###### Co-Author: [kazamahoang](http://www.codeforces.com/profile/phaicogiaiquocgia)
###### Tags: DP, Time Optimized, Fibonacci
-----
### Hướng dẫn
Nhận thấy $N \leq 2500$ và số truy vấn $Q \leq 15$ tạo điều kiện cho thuật quy hoạch động $O(n^2)$
Ý tưởng đơn giản là định nghĩa $f[i][j][p]$ là xét đến $[i]$, số cuối trong dãy là $[j]$, số trước đó trong dãy là $[p]$, tất nhiên $i > j > p$. Và chỉ thêm $[i]$ vào dãy khi $a[i] - a[j] = a[p]$ và gán $f[i][j][p] = max(f[j][p][q] + [a_i - a_j = a_p])$ với $[x = y]$ trả về $1$ khi đúng và $0$ khi sai.
Vì khi cố định $[i][j]$ ta biết được $[p]$. Ta giảm chiều bằng cách định nghĩa $f[i][j]$ là xét đến $[i]$, số cuối trong dãy là $[j]$. Và duyệt qua các vị trí $[p]$ có $a[i] - a[j] = a[p]$ để thêm vào dãy. Lúc này ta có $f[i][j] = max(f[j][p] + 1\ |\ a_i - a_j = a_p)$
Nhận thấy ta chỉ quan tâm độ dài. Và nó tăng dần qua mỗi $i$. Nên khi cố định $[i][j]$ thì vị trí có $p$ gần nhất sẽ dài hơn. Vậy ta gọi mảng $p[x]$ là vị trí số $p$ cuối cùng có tồn tại cặp $(i, j)$ mà $a_p = x = a_i - a_j$. Lúc này ta chỉ cần cập nhật $f[i][j] = max(f[j][p[a_i - a_j]] + 1\ ,\ 2)$ số $2$ đến từ việc dãy $2$ phần tử ${a_i, a_j}$ cũng hợp lệ theo đề bài.
:::warning
### Lưu ý:
- Vì bài này có cả số âm nên bạn phải dịch giá trị lên nếu như ngôn ngữ không hỗ trợ số âm.
- Vì bài này time limit chặt nên bạn phải cài khéo. Tức là tối ưu hằng số thời gian đủ nhỏ để qua.
:::
-----
### Code
> For $V = max(|a_i|)$
> **Constant:** $c_0 = \frac{1}{4}$
> **Time:** $O(q \times n^2 + V)$ total - $O(n^2 + n)$ query
> **Space:** $O(n^2 + V)$ total
> **Algo:** DP, Time Optimized
```cpp=
#include <iostream>
using namespace std;
const int LIM_N = 2501;
const int LIM_A = 2e6;
int n;
int a[LIM_N];
int f[LIM_N][LIM_N];
int p[2 * LIM_A + 1] = {};
int main()
{
ios::sync_with_stdio(NULL);
cin.tie(NULL);
int q;
cin >> q;
while (q-->0)
{
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int res = 2;
for (int i = 1; i <= n; ++i)
{
for (int j = i + 1; j <= n; ++j)
{
f[i][j] = max(2, f[p[a[j] - a[i] + LIM_A]][i] + 1);
if (res < f[i][j])
res = f[i][j];
}
p[a[i] + LIM_A] = i;
}
for (int i = 1; i <= n; ++i) p[a[i] + LIM_A] = 0;
cout << res << "\n";
}
return 0;
}
```