首页 > 其他分享 >浅谈 Nim game(尼姆博弈)

浅谈 Nim game(尼姆博弈)

时间:2023-12-23 09:02:40浏览次数:38  
标签:状态 xor 浅谈 Nim newa 异或 必胜 game operatorname

首先,我们需要了解 \(Nim\) 游戏是什么东西。

\(Nim\) 游戏指:两个人,有 \(n\) 堆数,每堆有 \(a_i\) 个,每次可以且仅可以取一堆中的若干个数,求问先手有没有必胜策略(当然两个人都足够聪明)。

首先,先研究显然的必胜策略。比如,我们要得到 \(0\) 这个数,那么当你取完时还剩下 \(0\) 个。

然后,我们通过最后的这个必胜状态,往前递推找出其余的必胜状态。

显然博弈论是必然会出现循环节的,如果每次都可以取到当前这个循环节上的必胜状态,并且让你的对手能达到的下个状态全部都是必败状态,那样你就稳赢。

接着,我们来分析 \(Nim\) 游戏这道题,我们先考虑什么状态下是必胜的:

当 \(n=1\) 的时候,很显然先手必胜,因为先手一次性取完即可。

  • 当然,这里当 \(a_1 ≠ 0\),所以可以理解为 \(a_1 \operatorname{xor} 0=a\)。

当 \(n=2\) 的时候:我们就需要考虑一堆的石头和另一堆的石头相不相等

  • 如果一堆的石头和另一堆相等,那么不论先手取什么,后手都只需要跟着先手取相同的个数就行了。所以很显然就是先手必败
    • 此时我们可以考虑为:\(a_1\operatorname{xor} a_2 = a\)。

如果一堆的石头和另一堆不相等,那么先手就取 \(|a_1-a_2|\)。就必胜了。

然后就转移到了上面那个状态,不过对象换了。

必胜是对于当前这个状态是必胜的,与是谁无关,赢的人只是处于一个胜的状态而已。这是先手必胜

于是,我们就将这个问题扩展到 \(n\) 上面。

如果当我取完后,每堆石头的数量为 \(0\),我胜。

  • 即最终的获胜式子为 \(0\operatorname{xor}0\operatorname{xor}0\operatorname{xor}…\operatorname{xor} 0=0\)。

  • 我们再来看看它的初始状态,即为 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\)。

  • 我们下一步期望状态应该是 \(newa_1\operatorname{xor}newa_2\operatorname{xor}newa_3\operatorname{xor}…\operatorname{xor}newa_n =a\)。

  • 所以很显然,如果任意的一个状态 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\) 都可以转化成 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =0\),那么这个状态的下一个状态一定是 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =aa\)。

  • 因为取一堆数的异或和为 \(0\),并且我们修改其中的一个元素,是无法维持 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =0\)。所以下一个状态(即你的对手)一定会变成一个 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =newa\) 的状态。

  • 但是如果你可以把 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =newa\) 的状态转变成 \(newa_1\operatorname{xor}newa_2\operatorname{xor}newa_3\operatorname{xor}…\operatorname{xor}newa_n =0\) 的状态,那么我们就可以把这一次一次操作当一个以 \(2\) 为循环周期的循环节。每次前者取到的值为 \(x(x ≠0)\),后者取到的值为 \(0\)。

  • 最后取到 \(0\operatorname{xor}0\operatorname{xor}0\operatorname{xor}…\operatorname{xor} 0=0\) (即获胜式子)这个式子的一定就是你。

  • 因为你的对手没有取到过异或和为 \(0\) 的状态,所以你是必胜的。

  • 接下来,我们证明为什么对于任何一个形似 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\) 的状态都可以转换成 \(newa_1\operatorname{xor}newa_2\operatorname{xor}newa_3\operatorname{xor}…\operatorname{xor}newa_n =0\) 的状态。

    • 首先,我们先分析一下整式 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\)。
    • 我们假设 \(a\) 的最高位 为 \(k\)。
    • 在异或运算中是不存在进位的,所以这个 \(1\) 肯定是第 \(k\) 位这一位上面算出来的,又因为它是异或,所以在前面的 \(a_1\) 到 \(a_n\) 中必然存在奇数个 \(1\)。
    • 我们只需要找到一个数这一位(第 \(k\) 位)为 \(1\) 就行了,多个的话只需要其中一个就行了,另外几个也是可行的方案。
    • 现在,我们的目的是将\(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\) 的状态转换成 \(newa_1\operatorname{xor}newa_2\operatorname{xor}newa_3\operatorname{xor}…\operatorname{xor}newa_n =0\) 的状态。所以说,如何在异或的一下吧 \(a\) 变成 \(0\) 呢?
    • 我们都知道,一个数异或上自己,答案是 \(0\)。那么我们对等式做一下变形,得到:
    • \(a\operatorname{xor}a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}a_4=a\operatorname{xor}a=0\)
    • 我们接下来要做的是把等式左边的 \(a\) 与一个 \(a_i\) 融合,使得此问题变小一堆,所以 \(a\operatorname{xor} a_i\) 要得到一个 \(<a_i\) 的新数。
    • 我们在这之前是不是找到了一个数在 \(a\) 的最高位(第 \(k\) 位)等于 \(1\),我们设它为 \(a_k\),它的第 \(k\) 位为 \(1\)。
    • 在第 \(k\) 位上,\(a_k\operatorname{xor}a=0\)。又因为 \(a_k\) 的前几位不变,第 \(k\) 位从 \(1 \rightarrow 0\),所以肯定变小了。 这满足了我们每次从一堆数中取出部分的要求。
    • 所以,对于任何一个形似 \(a_1\operatorname{xor}a_2\operatorname{xor}a_3\operatorname{xor}…\operatorname{xor}a_n =a\) 的状态都可以转换成 \(newa_1\operatorname{xor}newa_2\operatorname{xor}newa_3\operatorname{xor}…\operatorname{xor}newa_n =0\) 的状态的问题就被证明了。
  • 所以说,我们得到的结论是正确的。即,如果将每堆数相互异或得到的和不为 \(0\) ,则先手必胜

  • 反之,如果将每堆数相互异或得到的和为 \(0\) ,则先手必败。

  • 所以,我们可以得到代码。(题目:【模板】Nim 游戏

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
int main(){
	int T=read();
	while(T--){
		int n=read();
		int ans=0;
		for(int i=1;i<=n;++i){
			int x=read();
			ans^=x;
		}
		if(!ans){
			printf("No\n");
		}
		else{
			printf("Yes\n");
		}
	}
}

标签:状态,xor,浅谈,Nim,newa,异或,必胜,game,operatorname
From: https://www.cnblogs.com/Jasonshan10/p/17922676.html

相关文章

  • 浅谈单调栈
    单调栈,顾名思义,具有单调性的栈。单调,指满足一个序列是一个从小到大的序列或从大到小的序列。栈(\(stack\))是以一种线性存储结构,它具有以下特点:栈中的数据元素遵守“先进后出(\(First\in\Last\out\))”的原则,简称FILO结构;限定只能在栈顶进行插入和删除操作。所以,何为单......
  • 浅谈C++STL(Standard Template Library,标准模板库)
    2.1STL的诞生长久以来,软件界一直希望建立一种可重复利用的东西C++的面向对象和泛型编程思想,目的就是复用性的提升大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作为了建立数据结构和算法的一套标准,诞生了STL2.2STL基本概念STL(StandardTemplateLibrary,标......
  • 浅谈WPF之DataGrid过滤,分组,排序
    使用过Excel的用户都知道,Excel可以方便的对数据进行分组,过滤,排序等操作,而在WPF中,默认提供的DataGrid只有很简单的功能,那么如何才能让我们开发的DataGrid,也像Excel一样具备丰富的客户端操作呢?今天就以一个简单的小例子,简述如何在WPF中实现DataGrid的过滤,筛选,排序等功能。仅供学习分......
  • CF1881F Minimum Maximum Distance 题解
    因为白点对\(f_i\)没有贡献,所以可以重构出一棵原树的子树,使得所有的叶子都为标记点且标记点数量不变(没有删去标记点)。因为没有标记被删去且结构不变,所以这棵树的答案与原树答案相同。现在,对于所有节点,到它距离最大的标记点一定在叶子上。那么问题就变为:求出树上任意一点到所有......
  • Codeforces Round 651 (Div. 2)C. Number Game(数学思维数论)
    C.NumberGame我们考虑那些状态是必胜态我的回合时n为奇数(除1外),直接除以n则必胜下面偶数的情况稍复杂偶数我们能进行的操作只有除以一个奇数,需要考虑怎么把当前状态变为对手的必败态偶数一定含2的因子,\(n=2^k*q,q为奇数\)当\(k=1时如果q\)是一个质数那么只能除一次q这样......
  • Flutter AnimatedList 实现动态列表
    import'dart:async';import'package:flutter/material.dart';finalGlobalKey_globalKey=GlobalKey();classMyAnimatedListextendsStatelessWidget{constMyAnimatedList({super.key});@overrideWidgetbuild(BuildContextcont......
  • 浅谈C++类型转换函数
    reinterpret_castreinterpret_cast<newtype>(expression)将一个类型的指针转换为另一个类型的指针,它允许从一个指针转换为整数类型。voidtest01(){ chara=0; int*p=reinterpret_cast<int*>(&a); //不安全}const_cast常量const指针与普通指针之间的相互转化。如果不用......
  • P2197 【模板】Nim 游戏
    原题链接题解说的很详细,我来讲讲我对为什么要用异或判断的想法异或为零是先手必败状态的一个属性,我们通过属性来判断类别。代码#include<bits/stdc++.h>usingnamespacestd;intmain(){intt;cin>>t;while(t--){intn;cin>>n;......
  • E2. Game with Marbles (Hard Version)
    原题链接导论,有点博弈论的感觉?每个人轮流选一个大家都有的球,然后自己扣一个球,对方扣完。问女生剩下的球减去男生剩下的球,最大值是多少?一些条件1.初始每个人每种球都有2.女生想使答案值大一点,男生想使答案值小一点,换句话说,女生想使\(A\)值大一点,男生想使\(B\)值大一点,每个人都......
  • Unity无法显示animator面板,如何解决?
    步骤:点击动画的主体;右侧Inspector面板找到Animator,双击Controller中的对象;左上角即可显示animator面板。总结:不行就双击!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!遇事不决就双击~~......