Fundamentals of game theory (acwing)

First, understand several concepts:

NIM game: given N stacks of items, there are ai items in stack i. Two players take turns. They can choose a pile at a time and take away any number of items. They can take out a pile, but they can't take it without taking it. Taking the last item wins. Both took the best strategy and asked whether the first hand would win.

NIM also meets the following two game definitions: (these concepts are not important, just understand this idea)

Fair combination game ICG:

If a game meets:

1. Two players act alternately;

2. At any time in the game, the legal actions that can be performed have nothing to do with the player's turn; (so playing chess doesn't meet this condition)

3. Players who cannot act will be judged negative;

Directed graph game:

Given a directed acyclic graph, there is a unique starting point in the graph. There is a chess piece on the starting point. Two players alternately move the chess piece along the directional edge. They can move one step at a time, and those who cannot move will be judged negative.

NIM game can regard each state as a point. From the starting state, everyone moves it to the next state, and those who can't move are judged negative

Then we understand one thing

The first hand must win: in a certain case, the first hand has a state that after moving, the second hand can face a state that the first hand must lose;

Forerunner must lose: no matter how you move, the next state must be the state of forerunner must win;

Our forerunner will lose. There must be one (0,0,0... 0)

Then let's look at a proof:

Do you find that the above two conclusions are very similar to the saying that the first hand will win and the first hand will lose?

And the 000... 0 that must be lost first also corresponds to the starting condition 000... 0 with XOR 0

So we can think that if a1^a2 ^... ^ an=0, then this is a state in which the first hand will fail

If a1^a2 ^... ^ an=x (x! = 0), then this is a state of winning first

Let's look at a basic example:

  code:

#include <bits/stdc++.h>

using namespace std;

int main()
{
	int t;
	t=1;
	while (t--)
	{
		int ans;
		int n;
		cin >> n;
		for (int i = 0; i < n; i++)
		{
			int a;
			cin >> a;
			if (i)
			{
				ans ^= a;
			}
			else
			{
				ans = a;
			}
		}
		if (ans)
		{
			cout << "Yes" << endl;
		}
		else
		{
			cout << "No" << endl;
		}
	}
	return 0;
}

 

Next example:

  Idea:

Since this is a NIM problem, let's consider whether we can do it with XOR

1. For the stones on the even number, if the opponent moves the stones on the even number, no matter how many, we just need to take how many stones he takes to the next odd number step, and we can take as many stones from this odd number step to the next even number step, so as to ensure that the number of stones on the odd number step remains unchanged until all the stones on the even number step are 0;

2. It can be seen from 1 that we do not need to consider the stones on the even steps. As long as the number of stones on the odd steps is 0, the first hand will win;

Well, then we find that this is the XOR is 0. From our above conclusion: if a1^a3 ^... ^ a(2n-1) XOR is 0, the first hand cannot make all of them 0, that is, the first hand will lose; If a1^a3 ^... ^ a(2n-1) XOR is not 0, the first hand may make them all 0, that is, the first hand will win.

Upper Code:

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

int main()
{
	int n;
	cin >> n;
	int ans = 0;
	for (int i = 1; i <= n; i++)
	{
		int a;
		cin >> a;
		if (i % 2 != 0)
		{
			ans ^=a;
		}
	}
	if (ans == 0)
	{
		cout << "No";
	}
	else
	{
		cout << "Yes";
	}
	return 0;
}

Go on

Let's look at another question:

  This is different from the above two questions, but it is also a NIM game

To solve this problem, let's first look at several concepts:

Mex operation: let S represent a set of non negative numbers, and define mex (S) as the smallest non negative integer that does not belong to the set S (for example, mex{1,2,3,4}=0; mex{0,1,3,5}=2)

SG function: (I won't write the concept, for example)

  This is the sg function.

SG (x) = 0 at the starting node X of SG function, indicating that it cannot reach the state of 0; sg(x)!= 0 indicates that he can reach the state of 0. This does not satisfy any non-zero state, it is possible to go to 0, and it is necessary to win first; Any state that is 0 cannot be 0. If you meet the first hand, you will lose

Usually, there is more than one sg function and multiple starting nodes in the game, so we can XOR all sg. If it is 0, the first hand will lose; If it is not 0, the first hand will win. (here, all sg are regarded as stones, and the next state is regarded as taking stones, just like the above, using XOR)

For this problem, we can traverse the number that can be subtracted for each pile of stones to get an sg function, and find the sg, XOR of a pile of stones, which can be regarded as not 0;

Upper Code:

#include <bits/stdc++.h>
#include <unordered_ Set > / / header file of hash function
using namespace std;
const int N = 110, M = 10010;

int f[M], a[N];
int k;
int sg(int n)
{
	if (f[n] != -1)
	{
		return f[n];
	}
	unordered_set<int> S;//Establish a hash function to put sg the value of the next state, so that you can quickly query whether there is a number
	for (int i = 1; i <= k; i++)
	{
		if (n - a[i] >= 0)
		{
			S.insert(sg(n - a[i]));
		}
	}
	for (int i = 0;; i++)
	{
		if (!S.count(i))
		{
			return f[n] = i;
		}
	}
}

int main()
{
	int n;
	cin >>k;
	for (int i = 1; i <= k; i++)
	{
		cin >> a[i];
	}
	cin >> n;
	int ans = 0;
	memset(f, -1, sizeof(f));//Memory search, or sg will count many times
	for (int i = 1; i <= n; i++)
	{
		int res;
		cin >> res;
		ans ^= sg(res);
	}
	if (ans == 0)
	{
		cout << "No";
	}
	else
	{
		cout << "Yes";
	}
	return 0;
}

 

It's almost over. Hold on. One last thing:

This problem is to list the next state of each pile of stones, because it can decrease by 1 each time, so there are many states

SG (100) - > SG (99,99); SG (100) - > SG (98,99), etc

Then, just find the starting SG as above, but use a property: SG (x1, x2) is equivalent to splitting a situation into two situations. According to the SG function theory, the SG values of multiple independent situations are equal to the exclusive or sum of the SG values of these situations.

That is, sg (x1, x2) = sg (x1) ^ sg (x2)

code!

#include <bits/stdc++.h>
#include <unordered_set>
using namespace std;
const int N = 110;
int f[N];
int sg(int x)
{
	if (f[x] != -1)
	{
		return f[x];
	}
	unordered_set<int> S;
	for (int i = 0; i < x; i++)
	{
		for (int j = 0; j <= i; j++)
		{
			S.insert(sg(i) ^ sg(j));
		}
	}
	for (int i = 0;; i++)
	{
		if (!S.count(i))
		{
			return f[x] = i;
		}
	}
}

int main()
{
	int n;
	cin >> n;
	int ans = 0;
	memset(f, -1, sizeof(f));
	for (int i = 0; i < n; i++)
	{
		int a;
		cin >> a;
		ans ^= sg(a);
	}
	if (ans)
	{
		cout << "Yes";
	}
	else
	{
		cout << "No";
	}
	return 0;
}

OK, over!

 

Tags: Algorithm

Posted on Wed, 29 Sep 2021 00:24:26 -0400 by myfafa