AtCoder Beginner Contest 126 C - Dice and Coin
久しぶりのブログ更新、ねむーです。
今回はAtCoder(https://atcoder.jp/contests/abc126)にて開催されました、AtCoder Beginner Contest 126 C問題「C - Dice and Coin」の問題と僕との戦闘記です。
0.はじめに
1.問題文
すぬけ君は 1 〜 N の整数が等確率で出る N 面サイコロと表と裏が等確率で出るコインを持っています。すぬけ君は、このサイコロとコインを使って今から次のようなゲームをします。
まず、サイコロを 1 回振り、出た目を現在の得点とする。
得点が 1 以上 K − 1 以下である限り、すぬけ君はコインを振り続ける。表が出たら得点は 2 倍になり、裏が出たら得点は 0 になる。
得点が 0 になった、もしくは K 以上になった時点でゲームが終了する。このとき、得点が K 以上である場合すぬけ君の勝ち、 0 である場合すぬけ君の負けである。
N と K が与えられるので、このゲームですぬけ君が勝つ確率を求めてください。
2.制約
- 1 ≤ N ≤ 105
- 1 ≤ K ≤ 105
- 入力はすべて整数
3.入力例
- 入力
100000 5
- 出力
0.999973749998
4.初見の感想
- 毎回確率の係数を計算したくない
- 最初の数字がK以上なら無条件クリアとなる
- 確率の係数0.5をM回掛けるかは「Kを2で少なくともM回割れば最初の初期値以下になる」という条件で求められる(最初の初期値をどんどん2倍にするゲームなので…)
5.学びポイント
- 0.5を掛け算する回数は初期値が大きいほど少ないのでfor文をNから下に回します
- Nの値がKよりとても小さい場合、最初の0.5を何回掛けるかを出すのにループを回してしまう→ループを回さない工夫が必要
- Nで割る処理は値を非常に小さくしてしまうため、最後に行う(誤差を防止するため)
6.コードと簡単な解説
using System; using System.Collections.Generic; using System.Linq; class Program { static void Main(string[] args) { //入力 string[] input = Console.ReadLine().Split(' '); double N = double.Parse(input[0]); double K = double.Parse(input[1]); double ans = 0; double num = 1; //計算回数の少ないNからループを行う for(double i=N;i>0; i--) { if (i < K) { //係数は順番に*0.5ずつ小さくなる性質がある num *= 0.5; K = K / 2; } if(i>=K){ ans += num; } //ループを止める処理 if (ans == 0) { i = N + 1; } } //Nで割る処理は最後に行う ans = ans / N; Console.WriteLine(ans); } }
7.最後に
解けましたが1時間ほどかかってしまったので、鍛錬が必要です(^-^;