Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Mathematics / Game Theory

6.7.4 Grundy Numbers on DAG

6-Mathematics/6.7.4_Grundy_Numbers_on_DAG.cpp

Computes Grundy numbers for impartial games whose positions form a directed acyclic graph. Each node is a game position, and each outgoing edge is a legal move. Terminal nodes have Grundy number $0$; all other nodes take the MEX of their successors' Grundy numbers.

The implementation uses memoized DFS. It assumes the graph is acyclic; if cycles are present, the usual finite impartial-game Grundy recurrence is not directly valid without additional analysis.

  • grundy_on_dag(g) returns the Grundy number of every node in graph g, where g[u] contains all positions reachable from position u in one move.

Implementation

#include <vector>

std::vector<int> grundy_on_dag(const std::vector<std::vector<int>> &g) {
  std::vector<int> memo(g.size(), -1);
  auto dfs = [&](auto &&dfs, int u) -> void {
    if (memo[u] != -1) {
      return;
    }
    std::vector<char> seen(g[u].size() + 1);
    for (int v : g[u]) {
      dfs(dfs, v);
      int x = memo[v];
      if (x < static_cast<int>(seen.size())) {
        seen[x] = true;
      }
    }
    int res = 0;
    while (res < static_cast<int>(seen.size()) && seen[res]) {
      res++;
    }
    memo[u] = res;
  };
  for (int u = 0; u < static_cast<int>(g.size()); u++) {
    dfs(dfs, u);
  }
  return memo;
}

Example Usage

#include <cassert>
using namespace std;

int main() {
  vector<vector<int>> g{{1, 2}, {3}, {3, 4}, {}, {}};
  // Terminal nodes have Grundy 0, their predecessors have mex {0} = 1, and node 0 has mex {1} = 0.
  assert((grundy_on_dag(g) == vector<int>{0, 1, 1, 0, 0}));
  return 0;
}