[ICPC2020昆明C] Cities - 区间dp
Description
给定长度为 (n(nle 5000)) 的序列 (a),一次操作你可以选择一个数值相同的连续区间,将这个区间的数值修改为其他数。问多少进行多少次操作能使得区间所有数相同。每种 (a_i) 最多出现 (15) 次
Solution
首先我们先把相同连续的变成一个,这样相邻元素永远不相同
设 (f[i][j]) 表示把区间 ([i,j]) 搞成和位置 (j) 颜色一样,需要的最小代价
转移有两种
最后单独多一步,即 (f[i][j] leftarrow f[i][j-1]+1)
找中间一个和 (j) 颜色相同的位置 (l),(f[i][j] leftarrow f[i][l] + f[l+1][j])
这就是为什么题目中限制了每种元素的个数
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int m, s[N], n, a[N], pre[N], f[N][N], buf[N];
void solve()
{
cin >> m;
for (int i = 1; i <= m; i++)
cin >> s[i];
n = 0;
a[++n] = s[1];
for (int i = 2; i <= m; i++)
if (s[i] != s[i - 1])
a[++n] = s[i];
memset(buf, 0, sizeof buf);
for (int i = 1; i <= n; i++)
pre[i] = buf[a[i]], buf[a[i]] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
f[i][j] = 1e9;
for (int i = 1; i <= n; i++)
f[i][i] = 0, f[i + 1][i] = 0;
for (int len = 1; len < n; len++)
{
for (int i = 1; i + len <= n; i++)
{
int l = i, r = i + len;
f[l][r] = f[l][r - 1] + 1;
for (int p = pre[r]; p >= l; p = pre[p])
{
f[l][r] = min(f[l][r], f[l][p] + f[p + 1][r]);
}
}
}
cout << f[1][n] << endl;
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
solve();
}