競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 076D問題

ABC076D

解法

時刻と速度を軸としたグラフを書くと面積が距離となり, これを最大化する. 時刻 \(t\) と速度 \(v\) を状態とする DPができる. \(\ (t,v)\ \) が同じなら,次にできる遷移は同じになるため, そこに至るまでの道中を同一視できる.

簡単な問

速度制限無し ver. 丁度真ん中の時刻で折り返すと,答えが少数になることがある. 簡単のため,速度と時刻をそれぞれ2倍して考える. また,dp も遷移で三角形または台形の面積を使うので, 1/2 倍が出てしまう.そこで,dp の値も 2倍して保持することにして,

\( dp[t][v] = \) (2*距離)の最大値

とする.

今回の問

遷移に制限速度が付いた ver. 今の制限速度さえ求まれば遷移が書ける. 累積和を持ちながら \(t\) を動かせば, 今どの区間 \(i \in N\) にいるのかが分かる. それにより,制限速度 \(vs[i]\) も求まる. 制限速度が,時刻\(t\) までなのか,\(t+1\)までなのか,など 境界に気を付けて遷移する.

実装

面積を 8.0 で割って出力. 分母が 8までしか出ない計算なので, 最初から double で dp しても,おそらく AC出来る.

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

int main() {
  ll n; cin >> n;

  vll vs(n),ts(n);
  ll T = 0;
  rep(i,n){
    cin >> ts[i];
    ts[i] *= 2;
    T += ts[i];
  }
  ll V = 210; // V = T でも可能.
  rep(i,n){
    cin >> vs[i];
    vs[i] *= 2;
  }

  ll cum_t = 0, ind = 0;
  vll dp(V, -INFL);
  dp[0] = 0;
  rep(t,T){
    vll old(V, -INFL);
    swap(dp, old);
   
    if(t >= cum_t){
      cum_t += ts[ind];
      ind++;
    }
   
    rep(v, V) if(v <= vs[ind-1]){
      auto valid_v = [=](ll v) -> bool {
        return in(v,vs[ind-1]+1);
      };

      ll ne = old[v] + 2*v;
      chmax(dp[v], ne);
      if(valid_v(v-1)) chmax(dp[v-1], ne - 1);
      if(valid_v(v+1)) chmax(dp[v+1], ne + 1);
    }
  }

  double ans = dp[0]/ 8.0;
  printf("%.10lf\n", ans);


  return 0;
}