其实大家可以先看一下这个题
[USACO16OPEN]248
分析:
数据范围很奇特:n特别,a[i]特别——如果O(N^3)能接受就直接区间DP水过了,但是不行,于是考虑设计一个状态囊括a[i]。
注意到a[i]每次合并只+1,于是想到答案其实就是最大的被合并的数+1,并且可以按a[i]从小到大的顺序DP。
状态:f[i][j]表示从第j个数向右合并成数字i的下标的下一位。
状态转移方程:f[i][j] = f[i-1][f[i-1][j]]
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 270005; inline int read(){ char ch = getchar(); int f = 1 , x = 0; while(ch > '9' || ch < '0'){if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,a[maxn]; int f[60][maxn],ans; //f[i][j]:表示以j为左端点合成i的最后一个数的下标 int main(){ n = read(); for(int i=1;i<=n;i++) { a[i] = read(); f[a[i]][i] = i + 1; } for(int i=2;i<=41;i++) for(int j=1;j<=n;j++){ if(!f[i][j]) f[i][j] = f[i-1][f[i-1][j]]; if(f[i][j]) ans = i; } printf("%d",ans); return 0; }