题意描述
原题链接
Solution
首先想到一些性质
如果序列数全都不相同,则修改次数为n-1,若一段区间l,r端点数相同,
则一定是把中间的数修改成端点的值。且序列都不同,则可以在同一个操作下,修改成区间的任意一个数,同理,可以把区间相同的一段再看成一个点,则区间在最短的操作次数下,一定能修改成端点的值。
我
们
设
d
p
[
l
]
[
r
]
为
l
,
r
最
小
的
修
改
次
数
我们设dp[l][r]为l,r最小的修改次数
我们设dp[l][r]为l,r最小的修改次数
首先
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
+
1
]
[
r
]
+
1
,
d
p
[
l
]
[
r
−
1
]
+
1
)
dp[l][r]=min(dp[l+1][r]+1,dp[l][r-1]+1)
dp[l][r]=min(dp[l+1][r]+1,dp[l][r−1]+1),也就是区间拓展过来,不考虑任何特殊情况
若a[l]==a[r],则有,
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
r
]
,
d
p
[
l
+
1
]
[
r
−
1
]
+
1
)
dp[l][r]=min(dp[l][r],dp[l+1][r-1]+1)
dp[l][r]=min(dp[l][r],dp[l+1][r−1]+1),中间的数变成a[l]即可
再考虑若区间再中间的情况,即是
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]中存在
a
[
k
]
=
=
a
[
r
]
a[k]==a[r]
a[k]==a[r]
枚举每个
l
<
k
<
r
,
若
a
[
k
]
=
=
a
[
r
]
,
则
d
p
[
k
]
[
r
]
的
值
就
是
变
成
a
[
k
]
的
最
小
操
作
数
,
再
把
左
侧
区
间
d
p
[
l
]
[
k
−
1
]
变
成
a
[
k
]
即
可
l<k<r,若a[k]==a[r],则dp[k][r]的值就是变成a[k]的最小操作数,再把左侧区间\\ dp[l][k-1]变成a[k]即可
l<k<r,若a[k]==a[r],则dp[k][r]的值就是变成a[k]的最小操作数,再把左侧区间dp[l][k−1]变成a[k]即可
若
a
[
k
]
=
=
a
[
r
]
,
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
r
]
,
d
p
[
k
]
[
r
]
+
d
p
[
l
]
[
k
−
1
]
+
1
)
若a[k]==a[r],dp[l][r]=min(dp[l][r],dp[k][r]+dp[l][k-1]+1)
若a[k]==a[r],dp[l][r]=min(dp[l][r],dp[k][r]+dp[l][k−1]+1)
若
此
时
a
[
k
]
=
=
a
[
l
]
,
左
侧
也
不
需
要
那
个
+
1
,
因
为
本
身
就
是
a
[
k
]
若此时a[k]==a[l],左侧也不需要那个+1,因为本身就是a[k]
若此时a[k]==a[l],左侧也不需要那个+1,因为本身就是a[k]
故:
a
[
k
]
=
=
a
[
l
]
且
a
[
l
]
=
=
a
[
r
]
时
a[k]==a[l]且a[l]==a[r]时
a[k]==a[l]且a[l]==a[r]时,
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
r
]
,
d
p
[
l
]
[
k
]
+
d
p
[
k
]
[
r
]
)
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r])
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r])
其
实
仔
细
思
考
此
时
d
p
[
l
]
[
k
]
和
d
p
[
l
]
[
k
−
1
]
应
该
等
效
,
都
能
表
示
成
全
是
a
[
k
]
的
最
小
操
作
数
,
故
写
成
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
r
]
,
d
p
[
l
]
[
k
−
1
]
+
d
p
[
k
+
1
]
[
r
]
)
也
可
其实仔细思考此时dp[l][k]和dp[l][k-1]应该等效,\\都能表示成全是a[k]的最小操作数 ,故写成\\dp[l][r]=min(dp[l][r],dp[l][k-1]+dp[k+1][r])也可
其实仔细思考此时dp[l][k]和dp[l][k−1]应该等效,都能表示成全是a[k]的最小操作数,故写成dp[l][r]=min(dp[l][r],dp[l][k−1]+dp[k+1][r])也可
因
为
枚
举
k
会
n
3
,
而
其
实
每
个
数
最
多
出
现
15
,
我
们
只
需
要
存
一
下
每
个
数
上
一
次
出
现
位
置
就
行
时
间
复
杂
度
O
(
15
n
2
)
因为枚举k会n^3,而其实每个数最多出现15,我们只需要存一\\下每个数上一次出现位置就行\\ 时间复杂度O(15n^2)
因为枚举k会n3,而其实每个数最多出现15,我们只需要存一下每个数上一次出现位置就行时间复杂度O(15n2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#pragma GCC optimize(3, "Ofast", "inline")
#define fir(i, a, b) for (int i = a; i <= b; i++)
#define mov(i) (1ll << i)
#define MAX_INF 0x3f3f3f3f
const int maxn = 5e3 + 10;
int a[maxn];
int b[maxn];
int dp[maxn][maxn];
int pre[maxn], pos[maxn];
int dfs(int l, int r)
{
if (l == r)
return 0;
if (dp[l][r] != MAX_INF)
return dp[l][r];
dp[l][r] = min(dfs(l + 1, r) + 1, dfs(l, r - 1) + 1);
if (b[l] == b[r])
dp[l][r] = min(dp[l][r], dfs(l + 1, r - 1) + 1);
for (int k = pre[r]; k > l; k = pre[k])
{
if (b[l] == b[r])
dp[l][r] = min(dp[l][r], dfs(l, k - 1) + dfs(k + 1, r));
else
dp[l][r] = min(dp[l][r], dfs(l, k - 1) + dfs(k, r) + 1);
}
return dp[l][r];
}
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
fir(i, 1, n) scanf("%d", &a[i]);
int m = 0;
fir(i, 1, n) pre[i] = 0, pos[i] = 0;
fir(i, 1, n)
{
if (a[i] != b[m])
{
b[++m] = a[i];
}
}
n = m;
if (n == 1)
{
puts("0");
return 0;
}
fir(i, 1, n)
{
int &x = b[i];
pre[i] = pos[x];
pos[x] = i;
}
fir(i, 1, n)
fir(j, 1, n) dp[i][j] = MAX_INF;
printf("%d\n", dfs(1, n));
}
}