1. 程式人生 > 其它 >NC14055 C.Cities(區間 dp)

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;
}