题目:Cities
题意:给你一个长度为n的序列,a[i]为第i个的颜色,让他们颜色都相同,最少染色多少次,每一次可以将任意一区间染色成相同任意颜色的方块,但要求这个区间的方块颜色全部相同。
一开始dp[i][j]是第i块染色为j颜色的最少次数,发现无法处理3 2 1 2 3这样的数据,真正减少次数的贡献其实是当区间的边界点颜色相同时减少贡献,而题目也说了不超过15个一样的颜色。可以发现某一段区间染色都一样主要看最后一个的颜色是什么,区间dp[i][j]此区间全部染为颜色a[j]的最少次数,将连续且相同的区间变为一个,预处理一个当前点相同的颜色的前一点的位置,这样就能减少染色的次数,是一个最优的递推。
写的比较挫。
#include<cmath>
#include<iostream>
#include<sstream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
//#include<unordered_map>
#include<map>
#include<algorithm>
#include<queue>
#define mmp make_pair
#define inf 0x3f3f3f3f
#define llinf 0x7fffffffffffffff
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
typedef double lld;
const ll mod=998244353;
const int MAX=5e3+10;
int a[MAX];
int dp[MAX][MAX];
int pre[MAX],book[MAX];
vector<int>aa;
int main() {
int T;
scanf("%d",&T);
while(T--) {
aa.clear();
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
}
for(int i=1;i<=n;++i) {
int j=i+1;
while(j<=n && a[i]==a[j]) ++j;
aa.push_back(a[i]);
i=j-1;
}
n=aa.size();
for(int i=1;i<=n;++i) {
pre[i]=book[i]=0;
a[i]=aa[i-1];
}
for(int i=1;i<=n;++i) {
pre[i]=book[a[i]];
book[a[i]]=i;
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j)
dp[i][j]=inf;
dp[i][i]=0;
}
for(int len=2;len<=n;++len) {
for(int i=1;i+len-1<=n;++i) {
int j=i+len-1;
int k=j;
while(pre[k]>=i) {
int temp=pre[k];
if(a[i]==a[j])
dp[i][j]=min(dp[i][j],dp[temp][j]+dp[i][temp]);
else {
dp[i][j]=min(dp[i][j],dp[temp][j]+1+dp[i][temp-1]);
}
k=temp;
}
dp[i][j]=min(min(dp[i][j-1],dp[i+1][j])+1,dp[i][j]);
if(a[i]==a[j]) {
if(a[j-1]==a[j]) {
dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
}
else {
dp[i][j]=min(dp[i][j],dp[i+1][j-1]+1);
}
}
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}