NC14055 C.Cities(区间 dp)
目录
Description
有 \(n\) 个数,如果一个连续的区间数都相同,那么可以将它们全部变为另一个数,最后整个区间都变为同一个数的最小操作数是多少
State
\(1<=n<=5000\)
\(1<=a[i]<=n\)
Input
2
8
4 3 1 2 1 1 3 3
5
1 2 3 2 1
Output
3
2
Solution
如果一个区间 \([i+1,j]\) 上至少存在一个 \(k\),使得 \(a[i]=a[k]\) ,那么将 \([i+1,k]\) 全部变为 \(a[i]\) 的次数就是将 \([i,k]\) 全部变为 \(a[i]\) 的答案,而将 \([i,j]\) 全部变为 \(a[i]\) 还需要将 \([k+1,j]\) 变为 \(a[i]\), 也就是说 \([k+1,j]\) 区间上的数全部与 \(a[k]\) 相同,所以方程为
\[dp[i][j]=dp[i+1][k]+dp[k][j]
\]
如果这个区间上一个 \(k\) 也不存在,由于上述策略
\[dp[i][j]=dp[i+1][j]+1
\]
Code
const int N = 5000 + 5;
int n, m, k, _;
int a[N];
int dp[N][N];
int nxt[N], head[N];
void clear()
{
rep(i, 1, n) head[i] = 1e9, nxt[i] = 0;
for(int len = 1; len <= n; len ++){
for(int i = 1; i <= n; i ++){
int j = i + len - 1;
if(j > n) break;
dp[i][j] = 1e9;
}
}
}
void sol()
{
rep(i, 1, n) dp[i][i] = 0;
rep(i, 1, n - 1) dp[i][i + 1] = (a[i] != a[i + 1]);
for(int len = 3; len <= n; len ++){
for(int i = 1; i <= n; i ++){
int j = i + len - 1;
if(j > n) break;
dp[i][j] = dp[i + 1][j] + 1;
for(int k = nxt[i]; k <= j; k = nxt[k]){
dp[i][j] = min(dp[i][j], dp[i + 1][k] + dp[k][j]);
}
}
}
pd(dp[1][n]);
}
signed main()
{
// IOS;
rush(){
sd(n);
clear();
for(int i = 1; i <= n; i ++){
sd(a[i]);
if(a[i] == a[i - 1]){
i --;
n --;
}
}
for(int i = n; i; i --){
nxt[i] = head[a[i]];
head[a[i]] = i;
}
sol();
}
// PAUSE;
return 0;
}