Grundy numbers

Grundy numbers define the best move we can play in any state, let's represent Grundy Number of any state as G(Statei)

  • Recursively defined as G(Statei) = Mex{set of grundy numbers of all reachable states}, in our case G(i) = Mex{G(i-1), G(i-2)}
  • G(0) = G(1) = 0 is the base case

MEX ?

let us say we have a set of S = {1, 2, 3, 4, 7}

What is Mex(minimum excludant) of S?

It is smallest non negative number not present in S, Mex(S) = 0

Here are other examples:

Mex{0, 1, 4, 6} = 2;   Mex{1, 2, 3, 4} = 0;    Mex{0, 1, 2, 3, 4......wi} = wi+1

 

n(number of apples) Mex{reachable grundy values} Grundy Number/G(n) winning or loosing state
7 Mex{0, 2} 1 W
6 Mex{1, 2} 0 L
5 Mex{1, 1} 0 L
4 Mex{0, 1} 2 W
3 Mex{0}(cause the player can just eat 3 apples) 1 W
2 Mex{0}(cause the player can just eat 2 apples) 1 W
1 Mex{1} 0 L
0 - 0 L

Any state having grundy number 0 is a loosing state( try to wrap your head around this fact, it will soon get intuitive )

If Tintin starts with any state with a positive Grundy Number and play optimally Tintin wins, else he looses.

Here's an implementation of the above idea

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
# define FAST_CODE ios_base::sync_with_stdio(false); cin.tie(NULL);

constexpr ll MOD = 1000000007;

//to calculate Mex
ll Mex(unordered_set<ll>&S)
{
	ll Mex = 0;
	
	while (S.find (Mex) != S.end())
		Mex++;
	
	return (Mex);
}

//bottom up approach dp function to calulate grundy numbers
void nimber(ll n, vector<ll>&Grundy)
{

	Grundy[0] = 0; // base case
	Grundy[1] = 0; // base case

	for(ll i=2; i<=n; ++i)
	{
		unordered_set<ll> S;
		for(int j=2; j<=3; j++)
		{
			S.insert(Grundy[i-j]);	
		}

		Grundy[i] = Mex(S);
	}
}

int main()
{
	FAST_CODE
	#ifndef ONLINE_JUDGE
		freopen("input.txt","r", stdin);
		freopen("output.txt","w", stdout);
	#endif

	ll nums;
	cin>>nums; //number of apples in the box

	vector<ll>Grundy(nums+1, -1); //Grundy numbers

	nimber(nums, Grundy);

	for(ll g:Grundy)
	{
		cout<<g<<" "; // printing the grundy number
	}
		
	return 0;
}
Discussion