初心者向け
1 次元ランダムウォーク
数直線があって、原点 に点
がある。
時刻が 進むごとに、点
の位置が確率
で
、確率
で
動く。
この確率モデルを (対称な) 次元ランダムウォークと呼ぶ。
パスの個数
横軸を時間軸、縦軸を点 の位置を表すこととすると、以下のような折れ線グラフで表すことができる。
を、原点から点
までのパスの個数(言い換えると、時刻
に位置
に至る経路数)とする。
と
から、ランダムウォークで
した回数
と
した回数
をそれぞれ計算できる。
より、
となる。("45度回転"に相当する)
したがって と
の偶奇が一致しているとき
と二項係数を用いて表せる。
auto L = [&](int t, int x) -> modint { if(t % 2 != abs(x) % 2) { return 0; } else { return beet.C(t, (t + x) / 2); } };
鏡像の原理
点 、点
とする。ただし、
を満たす。
点 とする。これは点
と横軸 (
軸) に対して対称な点である。
点 から点
への道で、横軸と交点を持つものの個数は、点
から点
への道の個数に等しいことを示せる。
【お気持ち証明】 点 を点
からの経路で最初に横軸と交点を持つ点とする。 点
から
への経路を横軸に対して反転させると、
から
への経路になる。 つまり、点
から
の経路は、横軸と交点を持つ
から
への経路と 1 対 1 で対応している。
制限付きランダムウォーク
先に紹介した鏡像の原理を用いると、色々な制約下の経路数を求めることができる。
以下、点 、点
とする。ただし、
で
と
の偶奇は一致することを仮定する。
点 や
が上の制約を満たさない場合でも、適宜処理することでこれを満たすようにできる気がする(できなかったら諦めてネ)。
下限付きランダムウォーク
点の位置が常にある定数 以上であるような、点
から
への経路の個数を求めたいとする。
先に紹介した鏡像の原理を について適用すればよい。 全体の経路数
から
と交点を持つ経路数
をひいたものが答え。
auto low = [&](int t, int x, int l) -> modint { if(x < l) { return 0; } else { return L(t, x) - L(t, x - 2 * (l - 1)); } };
カタラン数
特に 、
とすると語らん数。
としてさっきの式に当てはめると、
となり、お馴染みの式となる。
上限付きランダムウォーク
点の位置が常にある定数 以下であるような、点
から
への経路の個数を求めたいとする。
横軸で上下反転させると、点の位置が常に 以上であるような、点
から
への経路と一致することがわかり、下限付きランダムウォークをすればよい。
auto high = [&](int t, int x, int u) -> modint { return low(t, -x, -u); };
右端で(厳密に)最大値をとるランダムウォーク
時刻 に初めて
に到達する(時刻
で最大値
をとる)ような点
から
への経路の個数を求めたいとする。
とした上限付きランダムウォークで
としたときの解と一致することがわかる。なぜなら時刻
で
にいるために時刻
で
にいて時刻
で
する必要があるため。
auto right_most = [&](int t, int x, int level) -> modint { return high(t - 1, x - 1, level - 1); };
例題
yukicoder No.660 家を通り過ぎないランダムウォーク問題
問題概要
次元の数直線で、原点にいる。
秒ごとに位置を
か
する。
秒以内に
に到達する経路数を求めてください。
解法
各 について、"時刻
にはじめてレベル
に到達する道の数" を求めて、すべて足し合わせる。
int N; cin >> N; modint ret = 0; for(int t = 1; t <= 2 * N; t++) { ret += right_most(t, N, N); } cout << ret << "\n";
AOJ 2335 10歳の動的計画
いにしえの有名問題
問題概要
次元グリッドが与えられて、
から
に右または上に
マス進むことを繰り返して到達する。
座標や
座標が負にならないように、ちょうど
回まで左または下に移動しながら到達する場合の経路数を求めてください。
解法
回左に移動、
回下に移動することにすると、実質
次元の問題に帰着できる。
これは " 次元の数直線で、原点から
を
回、
を
回するような経路のうち、原点より左に行かないものの個数" の問題が解ければ良いことがわかり、下限付きランダムウォーク。
int N, M, K; cin >> N >> M >> K; modint ret = 0; for(int i = 0; i <= K; i++) { int j = K - i; ret += low(N + 2 * i, N, 0) * low(M + 2 * j, M, 0) * beet.C(N + M + 2 * K, N + 2 * i); } cout << ret << "\n";
びーとさん
またあそぼうね
じょえさん
たぴちゃん
い木てる?
りゅーさん
一緒に無職をしましょう