memo[両方の位置が特定されていない組みの数][片方の位置が特定されていない組みの数] = 期待値
全ての位置を記憶できるという事は、あるターンで最初にめくるカードはまだ一度もめくっていない位置を選ぶのが最適。
絵が合わなかった場合、もう片方が出るまではずっと放置することになる。
"初めてめくるカード" -> "初めてめくるカード" (運良く同じ絵)
"初めてめくるカード" -> "初めてめくるカード" (違う絵)
"初めてめくるカード" -> "もう片方の位置が分かるカード"
"もう片方の位置が分かるカード" -> "もう片方のカード"
の4パターンを考える。
const int H = 50 + 1; const int W = 50 + 1; const int P = H * W; double memo[P][P]; double rec(int both, int half) { double& ret = memo[both][half]; if (ret != -1) return ret; if (both == 0 && half == 0) return 0; double sum = 0; double closed = both * 2 + half; double w = (closed - half) / closed; if (1 <= both) { // hit double p = 1.0 / (closed - 1.0) * w; sum += (1.0 + rec(both - 1, half)) * p; } if (2 <= both) { // miss double p = (closed - half - 2.0) / (closed - 1.0) * w; sum += (1.0 + rec(both - 2, half + 2)) * p; } if (1 <= both && 1 <= half) { // double p = 1.0 * half / (closed - 1.0) * w; sum += (2.0 + rec(both - 1, half)) * p; } if (half) { double p = 1.0 * half / closed; sum += (1.0 + rec(both, half - 1)) * p; } return ret = sum; } class PerfectMemory { public: double getExpectation(int N, int M) { fill(&memo[0][0], &memo[P - 1][P - 1] + 1, -1); return rec(N * M / 2, 0); }