なんか嘘くさいO(N)解法で通してしまった。
http://community.topcoder.com/stat?c=problem_statement&pm=13063
問題
1次元の無限に続くセルの列があり、初期状態でロボットが原点にいるとする。
Nターンそれぞれで1/2の確率で左右の隣接マスに移動する。
ロボットが通ったマスは赤く塗られる。
ターン数Nが与えられたとき、赤く塗られるマス数の期待値を答えよ。
解法
赤く塗られるマス数は(右端のセルの座標)-(左端のセルの座標)+1である。
するとさっと思いつくのは状態として[到達した左端座標][到達した右端座標][今の座標]を持つDPである。
ただ、このDPはO(N^4)かかるのでN<=500ではTLEする。
O(N^3)の解法
こちらは自分が本番で導けなかった解法。
最終的に欲しいのは(右端のセルの座標)-(左端のセルの座標)+1の値なので、左端と右端を個別に状態として管理する必要はない。
状態として[通過したセル数][今いる座標の到達した左端マスからの距離]とすることで計算量をO(N^3)に落とすことができる。
O(N)の解法
こちらは自分の本番解法。
O(N^3)解法が思いつかなかったので、とりあえず上記O(N^4)をN<=100程度まで解いて傾向を見てみた。
xターンの時の答えをP(x)とし、隣接項同士の差分を取ってみるとなんか法則が見えた。
P(1)-P(0)=1
P(2)-P(1)=0.5=1/2
P(3)-P(2)=0.5=1/2
P(4)-P(3)=0.375=3/8=(1/2)*(3/4)
P(5)-P(4)=0.375=3/8=(1/2)*(3/4)
P(6)-P(5)=0.3125=5/16=(1/2)*(3/4)*(5/6)
P(7)-P(6)=0.3125=5/16=(1/2)*(3/4)*(5/6)
偶数xごとに差分が(x-1)/x倍していることがわかる。
よってここから漸化式を求めてO(N)で求めることができる。
# ただ、なぜこの漸化式が成り立つのかはよくわからず。
以下のコードは両方の解法を含んでいる。
double dpdp[2][520][520]; class RedPaint { public: /* double expectedCells(int N) { int i; double dd[1010]; dd[0]=1; FOR(i,500) { dd[i*2+1]=dd[i*2+2]=dd[i*2]*(i*2+1)/(i*2+2.0); } double ret=1; FOR(i,N) ret+=dd[i]; return ret; } */ double expectedCells(int N) { ZERO(dpdp); int x,y,z,i; dpdp[0][0][0]=1; FOR(i,N) { int cur=i%2,tar=cur^1; ZERO(dpdp[tar]); FOR(x,i+1) FOR(y,i+1) { // right if(x==y) dpdp[tar][x+1][y+1] += dpdp[cur][x][y]/2; else dpdp[tar][x][y+1] += dpdp[cur][x][y]/2; // left if(y==0) dpdp[tar][x+1][y] += dpdp[cur][x][y]/2; else dpdp[tar][x][y-1] += dpdp[cur][x][y]/2; } } double ret=0; FOR(x,N+1) FOR(y,N+1) ret += dpdp[N%2][x][y]*(x+1); return ret; } };
まとめ
O(N)解法はあまり自身がなかったけど通ってしまった。