Div2 550, Div1 300 - RabbitStepping
問題
左から順に [0],[1],[2],[3],…[N-1] と番号を付けられたマスがあり,r 羽のウサギが1羽ずつどこかに入っている.以下の動作を繰り返す時,最後まで残っているウサギの数の期待値はいくらか?
- 以下のルールでウサギが1ステップ動く
- [0]にいたら[1]に移動する
- [N-1]か[N-2]にいたら左へ1マス移動する.
- 白マスにいたら左へ,黒マスにいたら右へ移動する
- 赤マスにいたら,赤マスの直前にいたマスへ移動する.ただし1ステップ目は左へ.
- 同じマスに 2 羽以上居るウサギは消される.
- N>2 であれば右端のマスが消えて,1 に戻る.
- 終了
考え方
Nの最大値は17なので全パターン(最大で 17C8=24310 通り)試せばOK.プログラム的には
- そのパターンをどう作るか
- シミュレーションが間に合うのか
という点が気になるが,どっちも実は大したことないので下手の考え休むに似たり.ガシガシ書きましょう.ただ,シミュレーションの方では解析的に答えが出ます.
まずは N マス中の r マスにウサギを配置するパターンの作り方.C++的には 1 を r 個,0 を (N-r) 個というvectorを作って next_permutation を利用するとコードもそう難しくなく実現できる.その他一般的にはビットを数える popCount 関数を作り,0≦i<2N の中から popCount(i)=r となる i の場合だけチェックすればいい.2k を示すビットが立っていれば [k] にウサギが居る,という設定ね,一応.
あとはシミュレーション.これは素直に書かれている通りやっても問題ない.が,解析的なお話.
- ウサギは毎回,[i] から [i-1] か [i+1] へ移動するので,[奇数] と [偶数] にいるウサギが干渉することは無い.
- ウサギが消えるには 2 羽以上が必要だが,1ステップの移動で1マスへ 3 羽以上のウサギが移動してくるのは不可能なので,ウサギが減る数は必ず(初期位置が[奇数][偶数]であったそれぞれのグループ別に見ても)偶数.
- 最後には 2 マスしか残らないので結局のところ初期位置が[奇数][偶数]だったグループからそれぞれ高々1羽しか残らない.
ということを考慮すると,初期位置の2グループそれぞれについて,奇数羽いたら 1 羽残り,偶数羽だったら残らない,ということになる.なのでシミュレーションなんて何のその.折角作った popCount を活用しよう.
コード
2パターン掲載.
#include <iostream> #include <vector> #include <algorithm> using namespace std; class RabbitStepping { int survive(vector<int> r, string f) { int n = f.size(); vector<int> pre(n, -1); for( int i = 1 ; i < n ; ++i ) pre[i] = i - 1; for( int m = n ; m >= 2 ; --m ) { f[m-1] = f[m-2] = 'W'; f[0] = 'B'; vector<int> nxt(m, 0), npre(m, -1); for( int i = 0 ; i < m ; ++i ) { if( r[i] == 0 ) continue; int dst = (f[i]=='W')?(i-1):(f[i]=='B')?(i+1):pre[i]; ++nxt[dst]; npre[dst] = i; } for( int i = 0 ; i < m ; ++i ) if( nxt[i] > 1 ) nxt[i] = 0; pre = npre; r = nxt; } return r[0] + r[1]; } public: double getExpected(string field, int r) { int n = field.size(); vector<int> rab(n, 0); for( int i = 0 ; i < r ; ++i ) rab[n-1-i] = 1; int all = 0, cnt = 0; do { cnt += survive(rab, field); ++all; } while( next_permutation(rab.begin(), rab.end()) ); return (double)cnt / all; } };
#include <string> using namespace std; class RabbitStepping { int popCount(int b) { b = ((b >> 1) & 0x55555) + (b & 0x55555); b = ((b >> 2) & 0x33333) + (b & 0x33333); b = ((b >> 4) & 0xf0f0f) + (b & 0xf0f0f); b = ((b >> 8) & 0xf00ff) + (b & 0xf00ff); return (b + (b >> 16)) & 0xffff; } int popOdd(int b) { b ^= b >> 16; b ^= b >> 8; b ^= b >> 4; b ^= b >> 2; return (b ^ (b >> 1)) & 1; } public: double getExpected(string field, int r) { int cnt = 0, all = 0; for( int i = 0 ; i < (1 << field.size()) ; ++i ) { if( popCount(i) == r ) { cnt += popOdd(i & 0x55555) + popOdd(i & 0xaaaaa); ++all; } } return (double)cnt / all; } };