想到了这一步(就是各种 dp 瞎搞):
for(int i=1;i<=n;i++)dp[0][i] = 0;
for(int i=0;i<n;i++){
for(int j=1;j<=n;j++){
if( dp[i][j] < 0 )continue;
if( i+1<=n && a[i+1] == j )dp[i+1][j] = max( dp[i+1][j], dp[i][j]+1 );
// if( i+3<=n && a[i+3] == j )dp[i+3][j] = max( dp[i+3][j], dp[i][j]+1 );
if( i+2<=n && a[i+1] != a[i+2] )dp[i+2][j] = max( dp[i+2][j], dp[i][j] );
}
}
感觉是个 dp, 当时想着要么不执行删除操作,
d
p
[
i
]
+
1
→
d
p
[
i
+
1
]
dp[i]+1 \rightarrow dp[i+1]
dp[i]+1→dp[i+1],执行操作的话要求就是相邻两个数不相同 balabala…
然后样例过不了,一是从长度为 0 的状态扩展到长度为 1 的状态 (个人喜欢称作从无到有的 active),但这要考虑区间
[
1
,
i
]
[\; 1, i\; ]
[1,i] 是否能全部删掉;二是
d
p
[
i
]
→
d
p
[
j
]
dp[i]\rightarrow dp[j]
dp[i]→dp[j] 的话,中间可能不止删两个,可能删一段,而每删删两个的话相邻的数就变了,不好判断。
然后又是经典的 “最大数量不超过总数一半” 系列,满足这个条件且长度为偶数的区间可以全部删掉,用一个数组 d e l [ i ] [ j ] del[i][j] del[i][j] 表示区间 [ i , j ) [i,j) [i,j) (左闭右开,这样可以表示长度为 0 的空区间) 可以通过若单次操作全部删掉,接下来就是个经典的 dp 了,不过最后答案获取有点特别,CF官方题解是用一个状态 d p [ n + 1 ] dp[n+1] dp[n+1] ;我就直接 遍历 + 判断更新
int a[5010],dp[5050];
bool del[5050][5050]; // del[i][j] -> [ i, j )
int cnt[5050];
int main() {
int T = 1;
scanf("%d",&T);
while(T--){
int n,m;
int k;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n+1;i++){ // 这里 i 要跑到 n+1
for(int i=1;i<=n;i++)cnt[i] = 0;
int Max = 0;
for(int j=i;j<=n+1;j++){
del[i][j] = false;
if( (j-i)%2 == 0 && Max <= (j-i)/2 ){
del[i][j] = true;
}
cnt[ a[j] ]++;
if( Max < cnt[ a[j] ] ) Max = cnt[ a[j] ];
}
}
dp[0] = 0;
for(int i=1;i<=n;i++){
// dp[i] = 1;
dp[i] = -1;
for(int j=0;j<i;j++){
if( ( j==0 || a[i] == a[j] ) && dp[j] >= 0 && del[j+1][i] ){
dp[i] = max( dp[i], dp[j]+1 );
}
}
}
// int ans = *max_element(dp+1,dp+n+1);
int ans = 0;
for(int i=1;i<=n;i++){
if( del[i+1][n+1] )ans = max( ans, dp[i] );
}
printf("%d\n",ans);
}
}