题目
一个长为n(n<=5e3)的序列a,
每次你可以选择两个相邻的位置,
只要这两个相邻的位置的值不同,就可以删掉这两个值
删完之后,后面的位置会接上来
问若干次操作后,在序列只剩同一种数字的前提下,序列的最长长度是多少
实际为t(t<=1e3)组样例,保证sumn不超过1e4
思路来源
夏老师
题解
想了一个枚举哪种字母最多,然后贪心删剩下的,兑掉其他字母,再往想留的字母上面兑,
但是假了,比如,aaaaaaaaabbbbbaaccccca,
想留a这种字母时,有可能中间的aa是要被牺牲掉的,bbbbbaaccccc整段删除
考虑终态局面是什么样子的
一定是某种相同的字母留在一些位置上
一定是选若干个区间,把每个区间都删干净了,剩下剩的是相同字母
所以,dp[i]表示考虑[1,i],i这种字母必留,考虑和ai相同的上一个必留的字母j在哪
能转移当且仅当[j+1,i)这段区间能被删干净,注意判断j+1==i的情况
特别地,dp[0]能转移到任意字母上,
为了统一写法,可以令任意字母也能转移到dp[n+1]上,
这样最后就不用遍历每个位置i判断[i+1,n]能否删完了
转移到dp[n+1]的时候长度加了1,减掉即可
代码
// #include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<vector>
// #include<map>
// #include<random>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=5e3+10,INF=0x3f3f3f3f;
int t,n,a[N],dp[N],cnt[N];//dp[i]表示只考虑[1,i]i必取的最多剩多少个
void ckmax(int &x,int y){
x=max(x,y);
}
int main(){
sci(t);
while(t--){
sci(n);
rep(i,1,n){
sci(a[i]);
}
rep(i,1,n+1){
dp[i]=-INF;
rep(j,1,n)cnt[j]=0;
int mx=0;
per(j,i,1){
int sz=i-j;
//[j,i-1]是可删完的,dp[j-1]就能转移到dp[i] 特判dp[i-1]->dp[i]
//dp[0]可以转移到任意颜色 任意颜色都能转移到dp[n+1]
if(sz%2==0 && 2*mx<=sz && (a[j-1]==a[i] || j-1==0 || i==n+1)){
dp[i]=max(dp[i],dp[j-1]+1);
}
cnt[a[j-1]]++;
mx=max(mx,cnt[a[j-1]]);
}
//printf("i:%d dp:%d\n",i,dp[i]);
}
printf("%d\n",dp[n+1]-1);
}
return 0;
}
/*
2 7 2 7 2
*/