[ICPC2020昆明C] Cities

[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();
}
原文地址:https://www.cnblogs.com/mollnn/p/14623673.html