m1une's library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:heavy_check_mark: Grundy Numbers
(game/grundy.hpp)

Overview

Computes the Sprague-Grundy number of every state in a finite directed acyclic impartial game. An edge v -> u means that a player may move from state v to state u. Terminal states receive Grundy number zero, and every other state receives the minimum excluded Grundy number of its successors.

The xor of the Grundy numbers of independent components is nonzero exactly when their disjoint sum is winning under normal play.

Functions

All functions are in namespace m1une::game.

Function signature Description Complexity
template<class Graph>
std::vector<int> grundy_numbers(const Graph& graph)
Returns one Grundy number per state. graph[v] lists states reachable from v. O(V + E) time, O(V) extra space

States and edges are zero-based. Every endpoint must be in [0, V), and the graph must be acyclic; both conditions are asserted in debug builds. Parallel edges are allowed and do not affect the result.

Example

#include "game/grundy.hpp"
#include <iostream>
#include <vector>

int main() {
    std::vector<std::vector<int>> moves(4);
    moves[0] = {1, 2};
    moves[1] = {3};
    moves[2] = {3};

    auto grundy = m1une::game::grundy_numbers(moves);
    std::cout << grundy[0] << '\n';  // 0
}

Required by

Verified with

Code

#ifndef M1UNE_GAME_GRUNDY_HPP
#define M1UNE_GAME_GRUNDY_HPP 1

#include <cassert>
#include <queue>
#include <vector>

namespace m1une {
namespace game {

// graph[v] contains the states reachable from v in one move.
// The graph must be a DAG.
template <typename Graph>
std::vector<int> grundy_numbers(const Graph& graph) {
    const int size = int(graph.size());
    std::vector<int> indegree(size);
    for (int vertex = 0; vertex < size; ++vertex) {
        for (int next : graph[vertex]) {
            assert(0 <= next && next < size);
            indegree[next]++;
        }
    }

    std::queue<int> queue;
    for (int vertex = 0; vertex < size; ++vertex) {
        if (indegree[vertex] == 0) queue.push(vertex);
    }
    std::vector<int> order;
    order.reserve(size);
    while (!queue.empty()) {
        const int vertex = queue.front();
        queue.pop();
        order.push_back(vertex);
        for (int next : graph[vertex]) {
            if (--indegree[next] == 0) queue.push(next);
        }
    }
    assert(int(order.size()) == size);

    std::vector<int> grundy(size);
    std::vector<int> seen(size + 1, -1);
    for (int position = size - 1; position >= 0; --position) {
        const int vertex = order[position];
        for (int next : graph[vertex]) {
            const int value = grundy[next];
            if (value <= size) seen[value] = vertex;
        }
        while (grundy[vertex] <= size && seen[grundy[vertex]] == vertex) {
            grundy[vertex]++;
        }
    }
    return grundy;
}

}  // namespace game
}  // namespace m1une

#endif  // M1UNE_GAME_GRUNDY_HPP
#line 1 "game/grundy.hpp"



#include <cassert>
#include <queue>
#include <vector>

namespace m1une {
namespace game {

// graph[v] contains the states reachable from v in one move.
// The graph must be a DAG.
template <typename Graph>
std::vector<int> grundy_numbers(const Graph& graph) {
    const int size = int(graph.size());
    std::vector<int> indegree(size);
    for (int vertex = 0; vertex < size; ++vertex) {
        for (int next : graph[vertex]) {
            assert(0 <= next && next < size);
            indegree[next]++;
        }
    }

    std::queue<int> queue;
    for (int vertex = 0; vertex < size; ++vertex) {
        if (indegree[vertex] == 0) queue.push(vertex);
    }
    std::vector<int> order;
    order.reserve(size);
    while (!queue.empty()) {
        const int vertex = queue.front();
        queue.pop();
        order.push_back(vertex);
        for (int next : graph[vertex]) {
            if (--indegree[next] == 0) queue.push(next);
        }
    }
    assert(int(order.size()) == size);

    std::vector<int> grundy(size);
    std::vector<int> seen(size + 1, -1);
    for (int position = size - 1; position >= 0; --position) {
        const int vertex = order[position];
        for (int next : graph[vertex]) {
            const int value = grundy[next];
            if (value <= size) seen[value] = vertex;
        }
        while (grundy[vertex] <= size && seen[grundy[vertex]] == vertex) {
            grundy[vertex]++;
        }
    }
    return grundy;
}

}  // namespace game
}  // namespace m1une
Back to top page