NC14055 C.Cities(区间 dp)

目录

Description

\(n\) 个数,如果一个连续的区间数都相同,那么可以将它们全部变为另一个数,最后整个区间都变为同一个数的最小操作数是多少

State

\(1<=n<=5000\)

\(1<=a[i]<=n\)

Input

2
8
4 3 1 2 1 1 3 3
5
1 2 3 2 1

Output

3
2

Solution

如果一个区间 \([i+1,j]\) 上至少存在一个 \(k\),使得 \(a[i]=a[k]\) ,那么将 \([i+1,k]\) 全部变为 \(a[i]\) 的次数就是将 \([i,k]\) 全部变为 \(a[i]\) 的答案,而将 \([i,j]\) 全部变为 \(a[i]\) 还需要将 \([k+1,j]\) 变为 \(a[i]\), 也就是说 \([k+1,j]\) 区间上的数全部与 \(a[k]\) 相同,所以方程为

\[dp[i][j]=dp[i+1][k]+dp[k][j] \]

如果这个区间上一个 \(k\) 也不存在,由于上述策略

\[dp[i][j]=dp[i+1][j]+1 \]


Code

const int N = 5000 + 5;
 
    int n, m, k, _;
    int a[N];
    int dp[N][N];
    int nxt[N], head[N];

void clear()
{
    rep(i, 1, n) head[i] = 1e9, nxt[i] = 0;
    for(int len = 1; len <= n; len ++){
        for(int i = 1; i <= n; i ++){
            int j = i + len - 1;
            if(j > n) break;
            dp[i][j] = 1e9;
        }
    }
}

void sol()
{
    rep(i, 1, n) dp[i][i] = 0;
    rep(i, 1, n - 1) dp[i][i + 1] = (a[i] != a[i + 1]);
    for(int len = 3; len <= n; len ++){
        for(int i = 1; i <= n; i ++){
            int j = i + len - 1;
            if(j > n) break;
            dp[i][j] = dp[i + 1][j] + 1;
            for(int k = nxt[i]; k <= j; k = nxt[k]){
                dp[i][j] = min(dp[i][j], dp[i + 1][k] + dp[k][j]);
            }
        }
    }
    pd(dp[1][n]);
}

signed main()
{
    // IOS;
    rush(){
        sd(n);
        clear();
        for(int i = 1; i <= n; i ++){
            sd(a[i]);
            if(a[i] == a[i - 1]){
                i --;
                n --;
            }
        }
        for(int i = n; i; i --){
            nxt[i] = head[a[i]];
            head[a[i]] = i;
        }
        sol();
    }
    // PAUSE;
    return 0;
}
posted @ 2021-11-08 11:08  Bcoi  阅读(35)  评论(0)    收藏  举报