数学: 博弈论. Nim游戏

C++

AcWing 891. Nim游戏

/*
题目描述:
	891. Nim游戏:
	给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
	问如果两人都采用最优策略,先手是否必胜。

	输入格式:
	第一行包含整数 n。
	第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量。

	输出格式:
	如果先手方必胜,则输出 Yes。
	否则,输出 No。

	数据范围:
	1 ≤ n ≤ 10^5,
	1 ≤ 每堆石子数 ≤ 10^9

解题思路:
	对于这种 Nim 游戏,首先需要考虑的是 必负态,
	如果能让对手到达 必负态 的就是必胜态,否则就是必负态,加入必负态集合中。

	从题目可知, n 堆石子全为 0 的状态就是必负态,本题不方便进行推导,我先给出公式后证明。
	设 n 堆石子数量为 a1, a2, ..., an,倘若 a1 ^ a2 ^ ... ^ an = 0,表示先手必负,否则先手必胜。(其中 ^ 表示二进制异或)
	
	证明:
		1. 当 n 堆石子 全为 0,是必负态,满足 a1 ^ a2 ^ ... ^ an = 0 的条件。
		2. 当 a1 ^ a2 ^ ... ^ an = x,并且 x != 0 时,我们来证明其是必胜态:
			a1 ^ a2 ^ ... ^ an = x 等式左侧,必定存在 ai,满足 x 的二进制最高位 1 在 ai 中也为 1。
			那么可得  (ai ^ x) < ai。因此 a1 ^ a2 ^ ... ^ an = x 等式两侧同时异或上 x,可得
			a1 ^ a2 ^ ... ^ an ^ x = 0
			a1 ^ a2 ^ ... ^ (ai ^ x) ^ ... ^ an = 0
			因此,我们在第 i 堆中可以拿出 ai - (ai ^ x)个石子,来使得剩余堆石子异或为 0
		3. a1 ^ a2 ^ ... ^ an = 0条件下,我们拿出任意个石子,都不可能保持 异或 等于 0 的条件
			假如说我们从第i堆取出一些石子(大于 0 ),数量从 ai 变为 bi ,我们假设异或仍旧等于 0
			a1 ^ a2 ^ ... ^ ai ^ ... ^ an = 0 
			a1 ^ a2 ^ ... ^ bi ^ ... ^ an = 0
			我们两式合并 (a1 ^ a2 ^ ... ^ ai ^ ... ^ an) ^ (a1 ^ a2 ^ ... ^ bi ^ ... ^ an) = 0
			化简之后可得 ai ^ bi = 0,因此可以得到 ai = bi,然而 bi 是严格小于 ai 的,所以说假设不成立。
		综上可知 a1 ^ ... ^ an = 0 为必负态,其余是必胜态。这是因为必胜态可以让对手达到必负态,必负态只能让对手达到必胜态
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;
int a[N], n;


bool solution() {
	int res = 0;
	for (int i = 1; i <= n; i ++ ) {
		res = (res ^ a[i]);
	}

	if (res == 0) {
		return false;
	} else {
		return true;
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++ ) {
		scanf("%d", &a[i]);
	}

	if (solution()) {
		puts("Yes");
	} else {
		puts("No");
	}

	return 0;
}


posted @ 2022-07-22 21:40  lucky_light  阅读(113)  评论(0编辑  收藏  举报