h_nosonの日記

競プロ、CTFなど

yukicoder No.75 回数の期待値の問題

問題
No.75 回数の期待値の問題 - yukicoder
1つのサイコロを繰り返し振り,出た目の合計をKにする.
もしKを上回ったら合計を0にリセットする.
サイコロの振る回数の期待値はいくらか.

解法1
絶対誤差か相対誤差が\pm0.01に収まればいいので
モンテカルロでも正解を出すことができる.

解法2
期待値DPと二分探索で解く.
期待値をmと決めてしまえば,Kを超えた時は残りm回でKにできることになるので,DPをした時に無限ループせず期待値を求めることができる.
x\geq0の時
\small dp[x]=1+\frac{1}{6}(dp[x-1]+dp[x-2]+dp[x-3]+dp[x-4]+dp[x-5]+dp[x-6])
x<0の時
dp[x]=m
この結果が元の決めた期待値より大きいか小さいかで二部探索をすれば求める期待値に近づけることができる.

ソースコード1

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

#define RREP(i,s,e) for (i = s; i >= e; i--)
#define rrep(i,n) RREP(i,n-1,0)
#define REP(i,s,e) for (i = s; i < e; i++)
#define rep(i,n) REP(i,0,n)
#define INF 100000000

typedef long long ll;

int main() {
    int i, k;
    double ans = 0;
    default_random_engine e;
    uniform_int_distribution<int> u{1,6};
    cin >> k;
    rep (i,500000) {
        int x = 0;
        int cnt = 0;
        while (x != k) {
            x += u(e);
            if (x > k)
                x = 0;
            cnt++;
        }
        ans += cnt;
    }
    cout << ans / 500000 << endl;
    return 0;
}

ソースコード2

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

#define RREP(i,s,e) for (i = s; i >= e; i--)
#define rrep(i,n) RREP(i,n-1,0)
#define REP(i,s,e) for (i = s; i <= e; i++)
#define rep(i,n) REP(i,0,n-1)
#define INF 100000000

typedef long long ll;

int main() {
    int i, j, k;
    double dp[201];
    double l, r;
    cin >> k;
    l = 0; r = 1200;
    while (l+0.01 < r && l*1.01 < r) {
        double m = (l+r) / 2;
        dp[0] = 0;
        REP (i,1,k) {
            dp[i] = 0;
            REP (j,1,6) {
                if (i-j < 0)
                    dp[i] += m;
                else
                    dp[i] += dp[i-j];
            }
            dp[i] /= 6;
            dp[i]++;
        }
        if (dp[k] < m)
            r = m;
        else
            l = m;
    }
    cout << (r+l)/2 << endl;
    return 0;
}