[ICPC2020昆明C] Cities - 区间dp

Description

给定长度为 \(n(n\le 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();
}

相关文章: