競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 314E

ABC314E

まず,0 の目が出ない様に変換しておく. 0 の個数を \(z\) 個とおく. 0 以外の目が出るまでの回数の期待値は \(\frac{p}{p-z}\) 回. \(c\) を \(c \cdot \frac{p}{p-z}\) で置き換えて,\(z\) を除いたルーレットで考えてよい.

\(e_{x}\) を,スコアが \(x\) のときの コストの最小値の期待値とする. \(x\) に到達するまでの出目は,今後に影響しない.
スコアを固定すると, このときの最善のルーレットの選び方が決まる. (最善とは限らない) ルーレットを選んだとき,どれくらい 寄与するのかを考える.
どのルーレットを使うか選ぶゲームと言える. 基本通り後退解析をするので,スコアが大きいほうから決めていく. スコア \(x\) とルーレット \(i\) を固定する. 出目 \(t\) 毎に \(\frac{e_{x+t}}{\vert s_{i} \vert} \) だけ \(e_{x}\) に寄与する. 1 回ルーレットを回すために \(c_{i}\) コストがかかるので, \(e_{x} = min_{i \in N}(\sum_{t \in s_{i}} \frac{e_{x+t}}{\vert s_{i} \vert} + c_{i})\) となる.

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

int main() {
  ll n,m;
  cin >> n >> m;
  vector<double> c(n), p(n);
  vector<vector<double>> s(n);
  rep(i,n){
    cin >> c[i] >> p[i];
    s[i].resize(p[i]);
    cinv(s[i]);

    ll z = 0;
    for(auto t: s[i]) if(t == 0) z++;
    c[i] *= p[i]/(double)(p[i] - z);
    p[i] -= z;
  }

 
  vector<double> e(m+1, 1e18);
  e[m] = 0;
  drep(x,m+1){
    rep(i,n){
      double sum = 0;
      for(auto t: s[i]) if(t){
        ll y = x+t;
        chmin(y, m);
        sum += e[y];
      }
      chmin(e[x], c[i] + sum/p[i]);
    }
  }

  printf("%.10lf\n", e[0]);


  return 0;
}