競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 317D

ABC317D

計算量を見ると,\(O(N * sum(Z))\) は間に合う. ここから DP を考えるというよりは,まずは自然に考える. 簡単に思いつくのは,\(i\) を固定したときの様子を探ること.

\(i \in N\) を固定したとき何人を鞍替えさせれば良いかを考える. 鞍替えさせる人数を \(d \in \mathbb{N}\) 人とする.

  • \(y_{i} < x_{i}\) のときは\(d = 0\)
  • \(y_{i} > x_{i}\) のときは \(y_{i} - d < x_{i} + d \) を満たせばよい. よって,\(d > (y_{i} - x_{i})/2\) が必要十分. 一番小さい取り方は,\(d = \lfloor (y_{i}-x_{i})/2 \rfloor + 1\).

つまり, \(i\) を固定すれば,何人鞍替えすれば良いかは一意に定まる.

これを踏まえると,次の DP が作れる.
\(dp_{i,s} := [0,i)\) まで調べて,議席 \(s\) を獲得するための最小の鞍替え人数.
これは部分和 DP の様に可能.

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

int main() {
  ll n;
  cin >> n;
  vll x(n), y(n), z(n);
  rep(i,n){
    cin >> x[i] >> y[i] >> z[i];
  }

  ll tot = 0;
  rep(i,n){
    tot += z[i];
  }

  vll dp(tot+1, INFL);
  dp[0] = 0;
  rep(i,n) {
   
    drep(s, tot+1){
      ll t = s + z[i];

      ll d = 0;
      if(y[i] > x[i]){
        d = (y[i]-x[i])/2 + 1;
      }

      if(t <= tot) chmin(dp[t], dp[s] + d);
    }
  }
 
 
  ll ans = INFL;
  srep(t, ceil(tot,2LL), tot+1){
    chmin(ans, dp[t]);
  }
  cout << ans << endl;

  return 0;
}