NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结
NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结
经典NIM游戏:
一共有N堆石子,编号1..n,第i堆中有个a[i]个石子。
每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。
两个人轮流行动,取走最后一个的人胜利。Alice为先手。
我们定义:
P:表示当前局面下先手必败
N:表示当前局面下先手必胜
N,P状态的转移满足如下性质:
1.合法操作集合为空的局面为P
2.可以移动到P的局面为N,这个很好理解,以为只要能转换到P局面,那么先手只需要使操作后变成P局面,那么后手就面临了一个必败的状态。
3.所有移动只能到达N的局面为P。无论怎么选取都会留给对手一个必胜状态。
其实知道这个之后应该是可以记忆化搜索或者用sg函数求解的,但是如果范围非常大,就没法做了。
就引进了nim游戏一个很神奇的结论:对于一个局面,当且仅当a[1] xor a[2] xor ...xor a[n]=0时,该局面为P局面,即必败局面。
证明如下:
1.全0的局面一定是P局面。
2.从任意一个异或值不为0(设为K)的局面一定可以转移到一个异或值为0的状态。由于异或计算的特殊性,我们知道一定有一个a[i]的某一位与k的最高位的1是相同的,那么必然有a[i] xor k<a[i],我们可以通过改变a[i[的值为a[i]',使a[1] xor a[2] xor a[i] xor ...xor a[n]=0
3.对于任意一个局面,若异或值为0,则不存在任何一个移动可以使新的局面的异或值为0.如果一位的异或值为0,那么这一位上一定有偶数个1,那么只改变一个数,一定无法使其保持0
Moore’s Nim:
n堆石子,每次从不超过k堆中取任意多个石子,最后不能取的人失败。
这是一个nim游戏的变形,也是有结论:把n堆石子的石子数用二进制表示,统计每个二进制位上1的个数,若每一位上1的个数mod(k+1)全部为0,则必败,否则必胜。
证明如下:
1.全为0的局面一定是必败态。
2.任何一个P状态,经过一次操作以后必然会到达N状态:在某一次移动中,至少有一堆被改变,也就是说至少有一个二进制位被改变。由于最多只能改变k堆石子,所以对于任何一个二进制位,1的个数至多改变k。而由于原先的总数为k+1的整数倍,所以改变之后必然不可能是k+1的整数倍。故在P状态下一次操作的结果必然是N状态。
3.任何N状态,总有一种操作使其变化成P状态。从高位到低位考虑所有的二进制位。假设用了某种方法,改变了m堆,使i为之前的所有位都回归到k+1的整数倍。现在要证明总有一种方法让第i位也恢复到k+1的整数倍。
有一个比较显然的性质,对于那些已经改变的m堆,当前位可以自由选择1或0.
设除去已经更改的m堆,剩下堆i位上1的总和为sum
分类讨论:
(1)sum<=k-m,此时可以将这些堆上的1全部拿掉,然后让那m堆得i位全部置成0.
(2)sum>k-m 此时我们在之前改变的m堆中选择k+1-sum堆,将他们的第i位设置成1。剩下的设置成0.由于k+1-sum<k+1-(k-m)<m+1,也就是说k+1-sum<=m,故这是可以达到的;
anti-nim反nim游戏:
正常的nim游戏是取走最后一颗的人获胜,而反nim游戏是取走最后一颗的人输。
一个状态为必胜态,当且仅当:
1)所有堆的石子个数为1,且NIM_sum(xor和)=0
2)至少有一堆的石子个数大于1,且 NIM_sum(xor和)≠0
题目:bzoj 1022: [SHOI2008]小约翰的游戏John
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
scanf("%d",&m);
for (int j=;j<=m;j++){
scanf("%d",&n);
int ans=; int pd=;
for (int i=;i<=n;i++){
int x; scanf("%d",&x);
if (x>) pd=;
ans^=x;
}
if (pd==&&!ans) printf("John\n");
else if (pd==&&ans) printf("John\n");
else printf("Brother\n");
}
}
Staircase NIM
顾名思义就是在阶梯上进行,每层有若干个石子,每次可以选择任意层的任意个石子将其移动到该层的下一层。最后不能操作的人输。
阶梯博弈经过转换可以变为Nim..把所有奇数阶梯看成N堆石子做nim。把石子从奇数堆移动到偶数堆可以理解为拿走石子,就相当于几个奇数堆的石子在做Nim。
假设我们是先手,所给的阶梯石子状态的奇数堆做Nim先手能必胜.我就按照能赢的步骤将奇数堆的石子移动到偶数堆.如果对手也是移动奇数堆,我们继续移动奇数堆.如果对手将偶数堆的石子移动到了奇数堆..那么我们紧接着将对手所移动的这么多石子从那个奇数堆移动到下面的偶数堆.两次操作后.相当于偶数堆的石子向下移动了几个。而奇数堆依然是原来的样子,即为必胜的状态。就算后手一直在移动偶数堆的石子到奇数堆,我们就一直跟着他将石子继续往下移,保持奇数堆不变。我可以跟着后手把偶数堆的石子最终移动到0,然后对手就不能移动这些石子了.所以整个过程.将偶数堆移动到奇数堆不会影响奇数堆做Nim博弈的过程..整个过程可以抽象为奇数堆的Nim博弈.
为什么是只对奇数堆做Nim就可以而不是偶数堆呢?因为如果是对偶数堆做Nim,对手移动奇数堆的石子到偶数堆,我们跟着移动这些石子到下一个奇数堆。那么最后是对手把这些石子移动到了0,我们不能继续跟着移动,就只能去破坏原有的Nim而导致胜负关系的不确定。所以只要对奇数堆做Nim判断即可知道胜负情况。
题目:http://poj.org/problem?id=1704
#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[N],p[N],b[N];
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&m);
for (int i=;i<=m;i++) {
scanf("%d",&n); int ans=;
int cnt=;
for (int j=;j<=n;j++) scanf("%d",&a[j]);
sort(a+,a+n+);
for (int j=;j<=n;j++) p[j]=a[j]-a[j-]-;
p[]=a[]-;
for (int j=;j<=n;j++) b[j]=p[n-j+];
for (int j=;j<=n;j++){
if (!b[j]) cnt++;
if (j&) ans^=b[j];
}
if (cnt==n) {
printf("Bob will win\n");
continue;
}
if (ans) printf("Georgia will win\n");
else printf("Bob will win\n");
}
return ;
}
新Nim游戏:
为使后手必败,先手留给后手的必然是若干线性无关的数字,否则后手可以留下一个异或和为零的非空子集使得先手必败,故问题转化为拿走和最小的数字使得留下的数线性无关,即留下和最大的线性基,这样拿走的数量显然最少,找到和最大的线性基只需贪心的把数字从大到小加入到基中即可(证明需用到拟阵)
例题:BZOJ3105
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll sum;
int k,num[],d[];
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') {if(ch=='-') f=-; ch=getchar();}
while(ch>=''&&ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
int Insert(int k)
{
for(int i=;i>=;--i)
{
if(k&(<<i))
{
if(!d[i]) {d[i]=k; return ;}
else k^=d[i];
}
}
return ;
}
bool cmp(int a,int b) {return a>b;} int main()
{
k=read();sum=;
for(int i=;i<=k;++i) num[i]=read();
sort(num+,num++k,cmp);
for(int i=;i<=k;++i) if(!Insert(num[i])) sum+=num[i]*1ll;
printf("%lld\n",sum);
return ;
}
威佐夫博弈:
两堆石子,每次可以取一堆或两堆,从两堆中取得时候个数必须相同,先取完的获胜。
这种情况下是颇为复杂的。我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示
两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们
称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,
10)、(8,13)、(9,15)、(11,18)、(12,20)。
可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k,奇异局势有
如下三条性质:
1。任何自然数都包含在一个且仅有一个奇异局势中。
由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak
-1 + k-1 = bk-1 > ak-1 。所以性质1。成立。
2。任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其
他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由
于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3。采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了
奇异局势(0,0);如果a = ak ,b > bk,那么,取走b – bk个物体,即变为奇异局
势;如果 a = ak , b < bk ,则同时从两堆中拿走 ak – ab – ak个物体,变为奇异局
势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余
的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k)
,从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – a
j 即可。
从如上性质可知,两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜
;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…,n 方括号表示取整函数)
题目:http://poj.org/problem?id=1067
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
double k=(+sqrt(5.0))/;
while(scanf("%d%d",&n,&m)!=EOF)
{
if (n>m) swap(n,m);
int t=m-n;
if (n==(int)((double)t*k)) printf("0\n");
else printf("1\n");
}
return ;
}
巴什博奕
只有一堆石子共n个。每次从最少取1个,最多取m个,最后取光的人取胜。
问先手是否有必胜策略,第一步该怎么取。
如果n=(m+1)*k+s (s!=0) 那么先手一定必胜,因为第一次取走s个,接下来无论对手怎么取,我们都能保证取到所有(m+1)倍数的点,那么循环下去一定能取到最后一个。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1846
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
int t;
scanf("%d",&t);
for (int i=;i<=t;i++){
scanf("%d%d",&n,&m);
if (n%(m+)) printf("first\n");
else printf("second\n");
}
return ;
}
NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结的更多相关文章
- 取石子游戏 HDU 1527 博弈论 威佐夫博弈
取石子游戏 HDU 1527 博弈论 威佐夫博弈 题意 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两 ...
- 洛谷 P2252 [SHOI2002]取石子游戏|【模板】威佐夫博弈
链接: P2252 [SHOI2002]取石子游戏|[模板]威佐夫博弈 前言: 第一眼大水题,第二眼努力思考,第 N 眼我是大水逼. 题意: 不看题目标题都应该能看出来是取石子类的博弈论. 有两堆石子 ...
- nim3取石子游戏 (威佐夫博弈)
http://www.cnblogs.com/jackge/archive/2013/04/22/3034968.html 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有 ...
- HDU2177:取(2堆)石子游戏(威佐夫博弈)
Problem Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同 ...
- 【POJ1067】取石子游戏 (威佐夫博弈)
[题目] Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的 ...
- HDU 1527 取石子游戏(威佐夫博弈)
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...
- HDU 1527 取石子游戏 (威佐夫博弈)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1527 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是 ...
- POJ.1067 取石子游戏 (博弈论 威佐夫博弈)
POJ.1067 取石子游戏 (博弈论 威佐夫博弈) 题意分析 简单的威佐夫博弈 博弈论快速入门 代码总览 #include <cstdio> #include <cmath> ...
- poj 1067 取石子游戏 (威佐夫博弈)
取石子游戏 http://poj.org/problem?id=1067 Time Limit: 1000MS Memory Limit: 10000K Description 有两堆 ...
随机推荐
- Ubuntu18.04.3 LTS初体验
安装系统 想来虚拟机安装太慢,正好有一台旧电脑,干脆整个乌班图系统. 启动盘工具:UltraISO,老牌工具了. 上官网下载ISO镜像文件: https://cn.ubuntu.com/downloa ...
- thinkphp volist标签中加if判断的写法
<if condition="$vo['devstatus'] eq 1">在线<else /> 离线</if> IF标签用法 <if c ...
- MySql——创建数据表,查询数据,排序查询数据
参考资料:<Mysql必知必会> 创建数据表 在学习前首先创建数据表和插入数据.如何安装mysql可以看看上个博客https://www.cnblogs.com/lbhym/p/11675 ...
- 十一、设备初始化(ADK4.0)
1.1 首先初始化连接库 sinkConnectionInit();à ConnectionInitEx2(); theCm.task.handler = connectionBluesta ...
- 【MongoDB】用Docker安装一个MongoDB最新版玩玩
1 安装 本文假设大家已经安装好了docker并能正常使用,所以不讲解如何安装docker了.用docker安装MongoDB最新版本如下: # 从repository查找mongo的相关镜像,结果很 ...
- MySQL锁会不会,你就差看一看
数据库锁知识 不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下),即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的.因为这 ...
- CSS中如果实现元素浮动和清除浮动,看这篇文章就足够了
浮动基本介绍 在标准文档流中元素分为2种,块级元素和行内元素,如果想让一些元素既要有块级元素的特点也同时保留行内元素特点,只能让这些元素脱离标准文档流即可. 浮动可以让元素脱离标准文档流,可以实现让多 ...
- mysql如何设置主从(读写分离),redis发布功能,以及redis的持久化存储(rdb,aof)
1 mysql基本命令 1.启动mysql systemctl start mariadb 2.linux客户端连接自己 mysql -uroot -p -h 127.0.0.1 3.远程链接mysq ...
- 【RN - 基础】之Windows下搭建React Native开发环境
前言 React Native由Facebook公司于2015年F8大会上开源,其主张“Learn once, write everywhere”.React Native的核心设计理念是:既拥有Na ...
- 【JavaEE】之MyBatis的ParameterType的使用
在MyBatis的Mapper.xml文件中,参数的表示方法有两种:一种是使用 “#{XXX}” 的方式表示的,另一种是使用 “${XXX}” 的方式表示的.今天来介绍以下这两种方式的不同之处. 1. ...