競技プログラミング日記

主に AtCoder の記事です

ABC275F

解法

\(a_{i}\) を残すとき 0, 消すとき 1 として,01 列を考える. 1 が連続している区間の個数を最小化する問題となる.
区間を数える代わりに,[01] の並びを数えると考えるとよい. [11], [00], [10] は数えないとする. つまり,01 列において [01] の並びの個数が 1 の区間の個数と 1対1 対応する.
これを踏まえれば,次の dp が作れる. \(dp_{i,s,k} := \) \(a_{[0,i)}\) において, 合計が \(s\) かつ 最後が \(k \in 2\) のときの min. ここで,\(a_{-1} = 0\) かつ \(-1\) 番目を残すと考えると実装が楽.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n,m;
  cin >> n >> m;
  vll a(n); rep(i,n) { cin >> a[i]; }

  vvll dp(m+1,vll(2,INFL));
  dp[0][0] = 0;
  rep(i,n){
    // 1: del
    vvll old(m+1,vll(2,INFL));
    swap(dp,old);

    drep(s,m+1){
      ll t = s + a[i];
      if(t <= m) chmin(dp[t][0], old[s][0]);
      if(t <= m) chmin(dp[t][0], old[s][1]);

      chmin(dp[s][1], old[s][0]+1);
      chmin(dp[s][1], old[s][1]);
    }
  }

  rep1(x,m){
    ll ans = min(dp[x][0],dp[x][1]);
    if(ans >= INFL) ans = -1;
    cout << ans << endl;
  }
 
  return 0;
}