题目链接
题目大意
给你长度为(n(nleq 5000))的数组(a(1le a[i]le n))
你每次可以选择一段连续且相同的元素使得它整体变为一个其他值
求你使得所有元素相等的最少操作次数
每个元素最多出现(15)次
题目思路
首先可以进行缩点操作
如果最开始相邻的一段元素相等,那么这一段可以等价为一个点
则可以变为有(m)个相邻元素不相等的点
假如这(m)个点都不同,那么答案就是(m-1)
而如果中间出现了有两个相同的点(l,r),那么最优的情况肯定是把([l+1,r-1])中的元素全部变为(a[r])
然后再整体操作([l,r])这样显然操作的次数更少,则区间在最短的操作次数下,一定能修改成端点的值
则可以设(dp[l][r])为把这个区间变为(a[r])的最少操作次数
而转移的话如果([l,r-1])中没有元素等于(a[r]),那么答案显然就是(dp[l][r-1]+1)
就是先把区间([l,r-1])的元素全部变为(a[r-1])然后再全部变为(a[r])
转移有可能比这个更优,因为([l,r-1])中可以出现(a[r])元素
那么就可以用这个进行中间转移
时间复杂度(O(n^2 imes 15))
这个有点难想,建议多想几遍
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=5e3+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,k;
int a[maxn];
int dp[maxn][maxn];
signed main(){
int _;scanf("%d",&_);
while(_--){
vector<int> vec[maxn];
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
vec[a[i]].push_back(i);
}
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++) dp[i][i]=0;
for(int l=n;l>=1;l--){
for(int r=l+1;r<=n;r++){
dp[l][r]=dp[l][r-1]+1;
for(int i=0;i<vec[a[r]].size();i++){
int x=vec[a[r]][i];
if(l<=x&&x<r){
dp[l][r]=min(dp[l][r],dp[l][x]+dp[x+1][r]);
}
}
}
}
printf("%d
",dp[1][n]);
}
return 0;
}