Disc Game Prize Fund
Source: https://projecteuler.net/problem=121
For a game with turns, the player wins only by drawing strictly more blue discs than red discs. Therefore, the number of blue discs required to win is
Let be the prize fund, and let
To decide how large can be, compare the banker’s certain income with the banker’s expected payout. The probability is the long-run fraction of games in which the banker must pay the prize. Thus, although any single game pays either or , the expected payout per game is .
Equivalently, the banker’s expected profit is the entry fee minus the expected prize payment, so it is . For the banker to avoid making an expected loss, we need , which means . Since must be a positive integer, the maximum valid prize fund is
It remains to compute .
Let denote the probability that exactly blue discs have been drawn after turns.
On turn , the bag contains blue disc and red discs. Therefore,
There are two ways to obtain exactly blue discs after turns.
First, the disc drawn on turn may be red. In that case, the first turns must already have contained exactly blue discs, which contributes
Second, the disc drawn on turn may be blue. In that case, the first turns must have contained exactly blue discs, which contributes
Therefore, the recurrence is
The base case is , with for . We also take whenever or .
For a 15-turn game, the player needs
blue discs to win. Hence
Now this can be formulated as a two-dimensional DP problem using .
using ld = long double;
void solve() {
ld dp[16][16] = {};
dp[0][0] = 1.0L;
for (int i = 1; i <= 15; ++i) {
for (int k = 0; k <= i; ++k) {
dp[i][k] += dp[i - 1][k] * ((ld)i / (i + 1));
if (k > 0) {
dp[i][k] += dp[i - 1][k - 1] * (1.0L / (i + 1));
}
}
}
ld p = 0.0L;
for (int k = 8; k <= 15; ++k) {
p += dp[15][k];
}
cout << floor(1 / p) << '\n';
}
This prints the maximum prize fund:
2269
Full CP-grade code
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
using ld = long double;
#pragma GCC optimize("Ofast", "inline", "fast-math", "unroll-loops", \
"no-stack-protector", "-ffast-math")
#pragma GCC target( \
"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native", "f16c")
static const bool Booster = []() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return true;
}();
auto init = []() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
return 'c';
}();
void solve() {
ld dp[16][16] = {};
dp[0][0] = 1.0L;
for (int i = 1; i <= 15; ++i) {
for (int k = 0; k <= i; ++k) {
dp[i][k] += dp[i - 1][k] * ((ld)i / (i + 1));
if (k > 0) {
dp[i][k] += dp[i - 1][k - 1] * (1.0L / (i + 1));
}
}
}
ld p = 0.0L;
for (int k = 8; k <= 15; ++k) {
p += dp[15][k];
}
cout << floor(1 / p) << '\n';
}
int main() {
solve();
return 0;
}