45th ICPC昆明 C.Cities(区间dp)

题目链接

Description

有n个点,每个点有自己的颜色(并且每种颜色出现次数不超过15次),每次操作可以把一段连续且颜色相同的点染成任意一种颜色,求把所有点染成相同颜色的最小操作次数。
\(1\leq n \leq 5000\)

Solution

首先我们可以贪心,把连续的一段缩成一个点。

区间\(dp\)\(dp[l][r]\)表示把\([l,r]\)这一段染成相同颜色的最小操作次数。
朴素的区间\(dp\)转移是\(n^3\)的。
但是这里有一个条件,每种颜色出现次数不超过15次,我们改变状态的定义,\(dp[l][r]\)表示把\([l,r]\)这一段染成与\(l\)点相同的颜色的最小操作次数,
预处理缩点之后的每个点的下一个相同颜色的点在哪个位置,利用这些点进行状态的转移。
假如与\(l\)点颜色相同的且在\([l,r]\)这一段的一些点是\(mid\),那么\(dp[l][r] = min(dp[l][r], dp[l][mid - 1] + dp[mid][r])\)

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;
using ll = long long;

void solve() {
    int n; cin >> n;
    vector<int> a(n + 1, -1), nxt(n + 1), pos(n + 1, n + 1);
    vector<vector<int>> dp(n + 2, vector<int> (n + 2, 1 << 29));
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        if (a[i] == a[i - 1] && i > 1) {
            i --, n --;
        }
    }
     for (int i = n;i > 0;i--) {
         nxt[i] = pos[a[i]];
         pos[a[i]] = i;
         dp[i][i] = 0;
    }
    for (int len = 1;len < n;len ++) {
        for (int l = 1;l + len <= n;l ++) {
            int r = l + len;
            dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]) + 1;
            int mid = nxt[l];
            while (mid <= r) {
                dp[l][r] = min(dp[l][r], dp[l][mid - 1] + dp[mid][r]);
                mid = nxt[mid];
            }
        }
    }
    cout << dp[1][n] << endl;
}
signed main(void) {
	cin.tie(nullptr), cout.tie(nullptr) -> ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t --)
	solve(); 
	return 0;
} 

另一种做法:
如果一段区间两个端点的颜色相同,那么我们对这一段区间染色时,相比于两个端点的颜色不同可以少一次染色操作,那么贪心的想,需要最大化区间两个端点颜色相同时的染色次数。

转移不是很彻底的懂,这种做法当作开拓思路了。

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;
using ll = long long;

void solve() {
    int n; cin >> n;
    vector<int> a(n + 1, -1), nxt(n + 1), pos(n + 1, n + 1);
    vector<vector<int>> dp(n + 1, vector<int> (n + 1, 0));
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        if (a[i] == a[i - 1] && i > 1) {
            i --, n --;
        }
    }
     for (int i = n;i > 0;i--) {
         nxt[i] = pos[a[i]];
         pos[a[i]] = i;
    }
    for (int len = 1;len < n;len ++) {
        for (int l = 1;l + len <= n;l ++) {
            int r = l + len;
            dp[l][r] = max(dp[l + 1][r], dp[l][r - 1]);
            int mid = nxt[l];
            while (mid <= r) {
                dp[l][r] = max(dp[l][r], dp[l + 1][mid - 1] + dp[mid][r] + 1);
                mid = nxt[mid];
            }
        }
    }
    cout << n - 1 - dp[1][n] << endl;
}
signed main(void) {
	cin.tie(nullptr), cout.tie(nullptr) -> ios::sync_with_stdio(false);
    int t; cin >> t;
    while (t --)
	solve(); 
	return 0;
} 

posted @ 2022-11-07 21:38  Coldarra  阅读(59)  评论(0)    收藏  举报