[2020 ICPC 昆明 C] Cities

https://ac.nowcoder.com/acm/contest/12548/C
题意:
现有\(n\)个城镇和\(n\)个国王,刚开始第\(i\)个城镇属于第\(a_i\)个国王。现在每次可以选一个下标连续且属于同一国王的城镇,将这些城镇归属到任意一个国王下面,问让所有城镇都属于一个国王,最少需要多少次这样的操作。

思路:
区间合并,试试看能否用区间\(dp\)进行操作。每次选取的区间肯定是越长越好,那么我们刚开始就把连续的,属于同一个国王的城镇区间缩成一个点。为了让能选的区间变得更长,要贪心地往左右区间合并。这时我们发现,如果左右区间的所属国王是相同的,那么我们只要修改中间这个区间的城镇,就能得到一个更长的区间。其余的就没区别了。那么我们预处理出每个区间的前一段和它属于同一个国王的区间在哪里,就能对这个区间进行状态转移了。

同时,我们这里是向前找上一个和最后的点相同的区间,所以对于所有的区间,我们都把它想象成向右合并的,才能得到最优解。

例如,对于\(1232\)这个区间,我们在进行合并的时候,\([1, 2]\)区间合并后当成属于第二个国王,那么我们从\([3, 4]\)往前找的时候,只用修改第三个城镇属于第二个国王,就能让所有城镇都一样了。

我感觉还是挺妙的,但是出题人说是常规区间\(dp\),果然是因为我没怎么做过区间\(dp\)吗。逃)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 5e3 + 7;

int dp[N][N];
int n, m;
int a[N], pre[N], lst[N];

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; ++i) {
        pre[i] = 0;
        lst[i] = 0;
    }
    m = 1;
    for (int i = 2; i <= n; ++i) {
        while (i <= n && a[i] == a[i - 1]) {
            i++;
        }
        if (i > n) break;
        a[++m] = a[i];
    }
    n = m;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            dp[i][j] = 0;
        }
    }
    
    for (int i = 1; i <= n; ++i) {
        pre[i] = lst[ a[i] ];
        lst[ a[i] ] = i;
    }

    for (int len = 1; len < n; ++len) {
        for (int i = 1; i <= n; ++i) {
            int j = i + len;
            if (j > n) break;
            dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
            for (int k = pre[j]; k >= i; k = pre[k]) {
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1] + 1);
            }
        }
    }
    printf("%d\n", dp[1][n]);
}

int main() {
    int t = 1;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}
posted @ 2021-04-30 14:01  stff577  阅读(258)  评论(0)    收藏  举报