CF 1839 D

Yaosicheng124發表於2024-09-16

題目描述

給定 \(N\) 個不同顏色的小球。你可以進行以下操作:

  • 插入一個顏色為 \(0\) 的小球,此操作最多執行 \(k\) 次。
  • 選擇一個非零球,使得該球與至少一個 \(0\) 小球相鄰。並把該小球移動到任意位置。這樣會花費 \(1\) 的代價。

對於每個 \(1\le k \le N\) 求出將序列變成一個去除 \(0\) 後升序的序列所需最小代價。

思路

定義 \(dp_{i,j}\) 表示把 \([1,i]\) 排序,使用 \(j\)\(0\) 小球所需的最小代價。

如果 \(a_i > a_{i-1}\),那麼我們有轉移 \(dp_{i,j}\leftarrow dp_{i-1,j}\)

否則,對於所有 \(0\le k < i-1且a_k<a_i\),那麼我們有 \(dp_{i,j}\leftarrow dp_{k,j-1}+i-k-1\),因為此時使用了一個 \(0\) 球,並且除端點外區間中每個點都需要移動一次。

空間複雜度 \(O(N^2)\),時間複雜度 \(O(N^3)\)

程式碼

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 505, INF = MAXN;

int t, n, a[MAXN], dp[MAXN][MAXN], ans;

void Solve() {
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 0; i <= n + 1; ++i) {
    for(int j = 0; j <= n; ++j) {
      dp[i][j] = INF;
    }
  }
  dp[0][0] = 0, a[n + 1] = n + 1;
  for(int i = 1; i <= n + 1; ++i) {
    for(int j = 0; j <= n; ++j) {
      if(a[i] > a[i - 1]) {
        dp[i][j] = dp[i - 1][j];
      }
      if(j) {
        for(int k = 0; k < i - 1; ++k) {
          if(a[k] < a[i]) {
            dp[i][j] = min(dp[i][j], dp[k][j - 1] + i - k - 1);
          }
        }
      }
    }
  }
  ans = dp[n + 1][0];
  for(int i = 1; i <= n; ++i) {
    ans = min(ans, dp[n + 1][i]);
    cout << ans << " \n"[i == n];
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}