備忘録

まとめておきたいことのメモ 主にプロコンのこと

ABC 011 D

今回はこの問題です。

abc011.contest.atcoder.jp

高橋君は上下左右に距離dだけ移動できます。それぞれ1/4の確率で移動します。初期値が(0, 0)で目標地点が(x, y)であるとき、ちょうどn回の移動で目的地点に辿り着ける確率はどれだけでしょうか、という問題です。

100点は得られましたが最後の最後でつまったので解説を見ました。解答は以下のようになりました。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <cstring> 
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <cstdlib>
    #include <map>
    #include <queue>
    #include <utility>
    #include <vector>
    #include <set>
    #include <memory.h>
    #include <iomanip>
    #include <bitset>
    #include <list>
    #include <stack>
     
    using namespace std;
     
    #define pai 3.14159265359
    #define mod 1000000007
     
    double solve(int n, int d, int x, int y)
    {
        if(x % d != 0 || y % d != 0 || (x + y) / d > n || (n - (x + y) / d) % 2 == 1) return 0.0;
        // cout << x / d << " " << y / d << endl;
        double kumiawase[1001][1001] = {};
        for(int i = 0; i <= n; i++){
            kumiawase[i][0] = 1;
        }
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= i; j++){
                kumiawase[i][j] = (4 * kumiawase[i - 1][j] + kumiawase[i - 1][j - 1]) * 0.25;
            }
        }
        double result = 0.0;
        for(int k = 0; k <= (n - (x + y) / d) / 2; k++){
            double tmp = kumiawase[n][x / d + k]; // 右
            tmp = tmp * kumiawase[n - x / d - k][y / d + (n - (x + y) / d) / 2 - k]; // 上
            tmp = tmp * kumiawase[(n - (x + y) / d) / 2][k]; // 左
            tmp = tmp * pow(0.25, (n - (x + y) / d) / 2 - k); // 下
            result += tmp;
        }
        return result;
    }
     
    int main()
    {
        int n, d, x, y;
        cin >> n >> d >> x >> y;
        x = abs(x);
        y = abs(y);
        double ans = solve(n, d, x, y);
        printf("%.10f\n", ans);
        return 0;
    }

xとyはあとで使う時正数のほうがいいので絶対値を取っておきます。
まず、xまたはyがdで割り切れない時、ジャンプ回数が足りない時、残りのジャンプ回数が2で割り切れない時(割り切れないと正しい位置にたどり着けません)は0.0を返すようにします。
次に組み合わせを求めます。kumiawase[i][j] = iCj * pow(0.25, j)となるようにしています。
目的地にたどり着くのに必要なジャンプ以外は、上下の組もしくは左右の組が合わせて(n - (x + y) / d) / 2回出るようにすれば目的地にたどり着けます。左右がk回、のこりは上下に飛ぶとして考えます。このとき右にはx/d + k、上にはy/d + (n - (x + y) / d) / 2 - k、左にはk、下には (n - (x + y) / d) / 2 - k回飛ぶことになります。あとはk = 0からk = (n - (x + y) / d) / 2までこの確率を求め、その和をとれば解になります。

nの最大値が1000ということで動的計画法で組み合わせの数がもとめられるじゃん!と思いましたが、int型では溢れてしまうんですね。なので確率と組み合わせておくことであふれずに計算できます。覚えておきたいなと思いました。