All Euler problems
Project Euler

Chef Showdown

A group of n chefs participate in a turn-based strategic cooking competition. On each chef's turn, he/she cooks a dish. Chef k has skill level S(k) = F_k / F_(n+1), where F_k is the k -th Fibonacci...

Source sync Apr 19, 2026
Problem #0481
Level Level 30
Solved By 304
Languages C++, Python
Answer 729.12106947
Length 732 words
linear_algebraprobabilitygame_theory

Problem Statement

This archive keeps the full statement, math, and original media on the page.

A group of chefs (numbered \(\#1\), \(\#2\), etc) participate in a turn-based strategic cooking competition. On each chef’s turn, he/she cooks up a dish to the best of his/her ability and gives it to a separate panel of judges for taste-testing. Let \(S(k)\) represent chef \(\#k\)’s skill level (which is publicly known). More specifically, \(S(k)\) is the probability that chef \(\#k\)’s dish will be assessed favorably by the judges (on any/all turns). If the dish receives a favorable rating, then the chef must choose one other chef to be eliminated from the competition. The last chef remaining in the competition is the winner.

The game always begins with chef \(\#1\), with the turn order iterating sequentially over the rest of the chefs still in play. Then the cycle repeats from the lowest-numbered chef. All chefs aim to optimize their chances of winning within the rules as stated, assuming that the other chefs behave in the same manner. In the event that a chef has more than one equally-optimal elimination choice, assume that the chosen chef is always the one with the next-closest turn.

Define \(W_n(k)\) as the probability that chef \(\#k\) wins in a competition with \(n\) chefs. If we have \(S(1) = 0.25\), \(S(2) = 0.5\), and \(S(3) = 1\), then \(W_3(1) = 0.29375\).

Going forward, we assign \(S(k) = F_k/F_{n+1}\) over all \(1 \le k \le n\), where \(F_k\) is a Fibonacci number: \(F_k = F_{k-1} + F_{k-2}\) with base cases \(F_1 = F_2 = 1\). Then, for example, when considering a competition with \(n = 7\) chefs, we have \(W_7(1) = 0.08965042\), \(W_7(2) = 0.20775702\), \(W_7(3) = 0.15291406\), \(W_7(4) = 0.14554098\), \(W_7(5) = 0.15905291\), \(W_7(6) = 0.10261412\), and \(W_7(7) = 0.14247050\), rounded to \(8\) decimal places each.

Let \(E(n)\) represent the expected number of dishes cooked in a competition with \(n\) chefs. For instance, \(E(7) = 42.28176050\).

Find \(E(14)\) rounded to \(8\) decimal places.

Problem 481: Chef Showdown

Mathematical Foundation

Definition 1 (Game state). A state is a pair (S,τ)(\mathcal{S}, \tau) where S{1,,n}\mathcal{S} \subseteq \{1, \ldots, n\} is the set of remaining chefs with S=m1|\mathcal{S}| = m \geq 1, and τ\tau is the index within the ordered set S\mathcal{S} indicating whose turn begins the next round.

Theorem 1 (Geometric round structure). Let the turn order within state (S,τ)(\mathcal{S}, \tau) be (c1,c2,,cm)(c_1, c_2, \ldots, c_m), and define qi=1S(ci)q_i = 1 - S(c_i) for each ii. A round consists of all mm chefs attempting a dish in sequence. Then:

(i) The probability that no chef succeeds in a full round is

Q(S)=i=1mqi.Q(\mathcal{S}) = \prod_{i=1}^{m} q_i.

(ii) The probability that chef cjc_j is the first to succeed in a given round is

Pj(S)=S(cj)i=1j1qi.P_j(\mathcal{S}) = S(c_j) \prod_{i=1}^{j-1} q_i.

(iii) These probabilities satisfy j=1mPj(S)=1Q(S)\sum_{j=1}^{m} P_j(\mathcal{S}) = 1 - Q(\mathcal{S}).

(iv) The number of completely failed rounds before the first success is geometrically distributed with parameter 1Q(S)1 - Q(\mathcal{S}).

Proof. Each chef’s success is an independent Bernoulli trial with parameter S(ci)S(c_i).

For (i): a complete failure occurs if and only if every chef fails, which has probability i=1mqi\prod_{i=1}^{m} q_i by independence.

For (ii): chef cjc_j is the first to succeed in a round if and only if c1,,cj1c_1, \ldots, c_{j-1} all fail and cjc_j succeeds. By independence, this has probability S(cj)i=1j1qiS(c_j) \prod_{i=1}^{j-1} q_i.

For (iii): we verify

j=1mS(cj)i=1j1qi=1i=1mqi.\sum_{j=1}^{m} S(c_j) \prod_{i=1}^{j-1} q_i = 1 - \prod_{i=1}^{m} q_i.

This is proved by induction on mm. For m=1m = 1: S(c1)=1q1S(c_1) = 1 - q_1. Assuming it holds for m1m - 1 chefs, adding the mm-th chef contributes S(cm)i=1m1qiS(c_m) \prod_{i=1}^{m-1} q_i, and the sum becomes (1i=1m1qi)+(1qm)i=1m1qi=1i=1mqi(1 - \prod_{i=1}^{m-1} q_i) + (1 - q_m) \prod_{i=1}^{m-1} q_i = 1 - \prod_{i=1}^{m} q_i.

For (iv): rounds are independent (each round’s outcome depends only on fresh Bernoulli trials), so the count of completely failed rounds before the first success follows Geom(1Q(S))\text{Geom}(1 - Q(\mathcal{S})). \square

Corollary 1. Conditioned on the first success occurring in some round, the probability that chef cjc_j is the one to succeed is

Pj(S)1Q(S).\frac{P_j(\mathcal{S})}{1 - Q(\mathcal{S})}.

Proof. Immediate from the law of total probability and the memoryless property of the geometric distribution. \square

Theorem 2 (Optimal elimination strategy). For each state (S,τ)(\mathcal{S}, \tau) and each chef cjSc_j \in \mathcal{S}, define W(cj,S,τ)W(c_j, \mathcal{S}, \tau) as the probability that cjc_j ultimately wins. When cjc_j succeeds and must choose whom to eliminate, the optimal target is

t=argmaxtS{cj}W(cj,S{t},τ(t)),t^* = \arg\max_{t \in \mathcal{S} \setminus \{c_j\}} W\bigl(c_j,\, \mathcal{S} \setminus \{t\},\, \tau'(t)\bigr),

with ties broken by choosing the chef whose turn comes soonest after cjc_j in the cyclic order, where τ(t)\tau'(t) denotes the appropriate starting index in S{t}\mathcal{S} \setminus \{t\}.

Proof. By backward induction on S|\mathcal{S}|.

Base case: S=1|\mathcal{S}| = 1. The remaining chef wins with probability 1; no elimination decision is required.

Inductive step: Suppose the winning probabilities W(,S,)W(\cdot, \mathcal{S}', \cdot) are uniquely determined for all states S\mathcal{S}' with S<S|\mathcal{S}'| < |\mathcal{S}|. When chef cjc_j succeeds in state (S,τ)(\mathcal{S}, \tau), eliminating target tt transitions the game to state (S{t},τ(t))(\mathcal{S} \setminus \{t\}, \tau'(t)), where τ(t)\tau'(t) is determined by the cyclic turn order (the next chef after cjc_j who is not tt). A rational agent maximizes W(cj,S{t},τ(t))W(c_j, \mathcal{S} \setminus \{t\}, \tau'(t)) over all valid targets tt. This is well-defined by the inductive hypothesis. The tie-breaking rule is deterministic and consistent. \square

Theorem 3 (Expected dish count). The expected number of dishes cooked from state (S,τ)(\mathcal{S}, \tau) satisfies the recurrence

E(S,τ)=Epartial(S,τ)1Q(S)+mQ(S)1Q(S)+j=1mPj(S)1Q(S)E(S{tj},τj),E(\mathcal{S}, \tau) = \frac{E_{\text{partial}}(\mathcal{S}, \tau)}{1 - Q(\mathcal{S})} + \frac{m \cdot Q(\mathcal{S})}{1 - Q(\mathcal{S})} + \sum_{j=1}^{m} \frac{P_j(\mathcal{S})}{1 - Q(\mathcal{S})} \cdot E(\mathcal{S} \setminus \{t_j^*\},\, \tau_j'),

where Epartial(S,τ)=j=1mjPj(S)E_{\text{partial}}(\mathcal{S}, \tau) = \sum_{j=1}^{m} j \cdot P_j(\mathcal{S}) is the expected number of dishes cooked within the successful round, tjt_j^* is the optimal elimination target for chef cjc_j, and τj\tau_j' is the resulting starting index.

Equivalently, combining the first two terms:

E(S,τ)=mQ(S)+Epartial(S,τ)1Q(S)+j=1mPj(S)1Q(S)E(S{tj},τj),E(\mathcal{S}, \tau) = \frac{m \cdot Q(\mathcal{S}) + E_{\text{partial}}(\mathcal{S}, \tau)}{1 - Q(\mathcal{S})} + \sum_{j=1}^{m} \frac{P_j(\mathcal{S})}{1 - Q(\mathcal{S})} \cdot E(\mathcal{S} \setminus \{t_j^*\},\, \tau_j'),

with the base case E({c},)=0E(\{c\}, \cdot) = 0 for any single chef cc.

Proof. Condition on the geometric number of failed rounds RGeom(1Q(S))R \sim \text{Geom}(1 - Q(\mathcal{S})). In each of the RR failed rounds (expected count Q/(1Q)Q/(1-Q)), exactly mm dishes are cooked, contributing mQ/(1Q)m \cdot Q/(1-Q) in expectation. In the successful round, chef cjc_j succeeds at position jj (i.e., after jj dishes in that round), with conditional probability Pj/(1Q)P_j/(1-Q). This gives the partial-round expected dish count Epartial/(1Q)E_{\text{partial}}/(1-Q).

After the elimination, the game transitions to state (S{tj},τj)(\mathcal{S} \setminus \{t_j^*\}, \tau_j') and the expected remaining dishes are E(S{tj},τj)E(\mathcal{S} \setminus \{t_j^*\}, \tau_j'), weighted by the conditional probability Pj/(1Q)P_j/(1-Q).

The base case E({c},)=0E(\{c\}, \cdot) = 0 holds because no more dishes are cooked once a single chef remains. \square

Editorial

Chef k has skill S(k) = F_k / F_{n+1} (Fibonacci ratio). Chefs eliminate each other optimally. Compute E(14), the expected number of dishes cooked. Method: bitmask DP over game states (remaining chefs, start index). For each state, compute winning probabilities via Theorem 2 (backward induction on optimal elimination) and expected dishes via Theorem 3 (geometric round structure + recursive decomposition).

Pseudocode

    Compute Fibonacci numbers F[1..n+1]
    S[k] = F[k] / F[n+1] for k = 1..n

    States are (bitmask of remaining chefs, start index within the ordered set)

    For each each state (mask, start_idx) in order of increasing popcount(mask):
        If popcount(mask) == 1 then
            W[mask][start_idx][lone_chef] = 1.0
            E[mask][start_idx] = 0.0
            continue

        chefs = sorted list of set bits in mask
        m = len(chefs)
        Reorder chefs cyclically starting at start_idx
        Compute Q, P[j], and reach probabilities p_reach[j]

        For each chef c_j, find optimal target t_j* via argmax of W
        (with tie-breaking by cyclic distance)

        Compute W[mask][start_idx][k] for all k via weighted sum
        Compute E[mask][start_idx] via Theorem 3

    Return E[full_mask][0]

Complexity Analysis

  • Time: O(n22n)O(n^2 \cdot 2^n). There are O(n2n)O(n \cdot 2^n) states (mask, start); for each state with mm chefs, evaluating all elimination choices costs O(m)O(m) per chef, yielding O(m2)O(m^2) per state. Summing over all masks: masksm2=O(n22n)\sum_{\text{masks}} m^2 = O(n^2 \cdot 2^n). For n=14n = 14, this is approximately 142×163843.2×10614^2 \times 16384 \approx 3.2 \times 10^6 operations.
  • Space: O(n2n)O(n \cdot 2^n) for storing winning probabilities W[mask][start][chef]W[\text{mask}][\text{start}][\text{chef}] and expected dishes E[mask][start]E[\text{mask}][\text{start}].

Answer

729.12106947\boxed{729.12106947}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_481/solution.cpp
/*
 * Project Euler Problem 481: Chef Showdown
 *
 * Chef k has skill S(k) = F_k / F_{n+1}. Chefs eliminate each other optimally.
 * Compute E(14) via bitmask DP over game states (remaining chefs, start index).
 *
 * Theorems applied:
 *   - Geometric round structure (Theorem 1): first-success probabilities P_j
 *   - Optimal elimination (Theorem 2): backward induction on win probabilities
 *   - Expected dishes (Theorem 3): geometric + recursive decomposition
 */

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 14;
double fib[MAXN + 2];
double skill[MAXN];

void init_fib(int n) {
    fib[1] = fib[2] = 1;
    for (int i = 3; i <= n + 1; i++)
        fib[i] = fib[i - 1] + fib[i - 2];
}

vector<int> get_chefs(int mask, int n) {
    vector<int> v;
    for (int i = 0; i < n; i++)
        if (mask & (1 << i)) v.push_back(i);
    return v;
}

int find_next_turn(const vector<int>& order, int j, int elim_chef) {
    int m = order.size();
    for (int k = j + 1; k < m; k++)
        if (order[k] != elim_chef) return order[k];
    for (int k = 0; k < j; k++)
        if (order[k] != elim_chef) return order[k];
    return -1;
}

map<pair<int,int>, vector<double>> memo_win;
map<pair<int,int>, double> memo_exp;

vector<double> compute_win(int mask, int start_idx, int n);
double compute_exp(int mask, int start_idx, int n);

vector<double> compute_win(int mask, int start_idx, int n) {
    auto key = make_pair(mask, start_idx);
    auto it = memo_win.find(key);
    if (it != memo_win.end()) return it->second;

    vector<int> chefs = get_chefs(mask, n);
    int m = chefs.size();

    if (m == 1) {
        vector<double> w(n, 0);
        w[chefs[0]] = 1.0;
        return memo_win[key] = w;
    }

    vector<int> order;
    for (int i = start_idx; i < m; i++) order.push_back(chefs[i]);
    for (int i = 0; i < start_idx; i++) order.push_back(chefs[i]);

    vector<double> p_reach(m);
    p_reach[0] = 1.0;
    for (int j = 1; j < m; j++)
        p_reach[j] = p_reach[j - 1] * (1.0 - skill[order[j - 1]]);
    double p_none = p_reach[m - 1] * (1.0 - skill[order[m - 1]]);

    vector<double> result(n, 0);
    for (int j = 0; j < m; j++) {
        double p_j = p_reach[j] * skill[order[j]] / (1.0 - p_none);

        double best_wp = -1;
        int best_e = -1;
        vector<double> best_w;

        for (int e = 0; e < m; e++) {
            if (e == j) continue;
            int elim = order[e];
            int new_mask = mask ^ (1 << elim);
            vector<int> nc = get_chefs(new_mask, n);

            int nxt = find_next_turn(order, j, elim);
            int ns = 0;
            if (nxt >= 0)
                for (int k = 0; k < (int)nc.size(); k++)
                    if (nc[k] == nxt) { ns = k; break; }

            vector<double> w = compute_win(new_mask, ns, n);
            if (w[order[j]] > best_wp + 1e-15) {
                best_wp = w[order[j]];
                best_e = e;
                best_w = w;
            } else if (fabs(w[order[j]] - best_wp) < 1e-15) {
                if ((e - j + m) % m < (best_e - j + m) % m) {
                    best_e = e;
                    best_w = w;
                }
            }
        }

        for (int k = 0; k < n; k++)
            result[k] += p_j * best_w[k];
    }

    return memo_win[key] = result;
}

double compute_exp(int mask, int start_idx, int n) {
    auto key = make_pair(mask, start_idx);
    auto it = memo_exp.find(key);
    if (it != memo_exp.end()) return it->second;

    vector<int> chefs = get_chefs(mask, n);
    int m = chefs.size();

    if (m == 1) return memo_exp[key] = 0;

    vector<int> order;
    for (int i = start_idx; i < m; i++) order.push_back(chefs[i]);
    for (int i = 0; i < start_idx; i++) order.push_back(chefs[i]);

    vector<double> p_reach(m);
    p_reach[0] = 1.0;
    for (int j = 1; j < m; j++)
        p_reach[j] = p_reach[j - 1] * (1.0 - skill[order[j - 1]]);
    double p_none = p_reach[m - 1] * (1.0 - skill[order[m - 1]]);

    double exp_partial = 0;
    for (int j = 0; j < m; j++)
        exp_partial += (j + 1) * p_reach[j] * skill[order[j]];
    exp_partial /= (1.0 - p_none);

    double exp_dishes = p_none / (1.0 - p_none) * m + exp_partial;

    double exp_after = 0;
    for (int j = 0; j < m; j++) {
        double p_j = p_reach[j] * skill[order[j]] / (1.0 - p_none);

        double best_wp = -1;
        int best_e = -1, best_ns = 0, best_nm = 0;

        for (int e = 0; e < m; e++) {
            if (e == j) continue;
            int elim = order[e];
            int new_mask = mask ^ (1 << elim);
            vector<int> nc = get_chefs(new_mask, n);

            int nxt = find_next_turn(order, j, elim);
            int ns = 0;
            if (nxt >= 0)
                for (int k = 0; k < (int)nc.size(); k++)
                    if (nc[k] == nxt) { ns = k; break; }

            vector<double> w = compute_win(new_mask, ns, n);
            if (w[order[j]] > best_wp + 1e-15) {
                best_wp = w[order[j]];
                best_e = e;
                best_ns = ns;
                best_nm = new_mask;
            } else if (fabs(w[order[j]] - best_wp) < 1e-15) {
                if ((e - j + m) % m < (best_e - j + m) % m) {
                    best_e = e;
                    best_ns = ns;
                    best_nm = new_mask;
                }
            }
        }

        exp_after += p_j * compute_exp(best_nm, best_ns, n);
    }

    return memo_exp[key] = exp_dishes + exp_after;
}

int main() {
    int n = 7;
    init_fib(n);
    for (int k = 0; k < n; k++)
        skill[k] = fib[k + 1] / fib[n + 1];
    printf("E(7) = %.8f\n", compute_exp((1 << n) - 1, 0, n));

    memo_win.clear();
    memo_exp.clear();
    n = 14;
    init_fib(n);
    for (int k = 0; k < n; k++)
        skill[k] = fib[k + 1] / fib[n + 1];
    printf("E(14) = %.8f\n", compute_exp((1 << n) - 1, 0, n));

    return 0;
}