NIM游戏:
两人玩家,给定状态之间转移规则,每个人轮流移动,最终得出胜负。
设P为必败点,N为必胜点
精髓: 能到达必败点的所有点都为必胜点,一般解题都是先找到一个必败点,然后由此点推必胜点

有的题目不是巴什博奕的类型,在取物品上有特定的取法,需要用sg函数来解

若题目中只讨论一堆物品 然后让判断先手必胜还是必败,一般就用一个数组SG[n]打表,其中n是该物品的数量,通过递推求出所有范围内的SG[]的值,为0时必败,为1时必胜

1.对一类题目,两人轮流抓牌类,每次抓取方式有多种(斐波那契数、幂函数等可用数组存取)
定义mex运算,表示最小的不属于这个集合的非负整数,运用SG打表
例如:mex{0,1,2,4}=3 mex{2,3,5}=0 mex{}=0
对于任意状态的x,定义SG(x)=mex(S) ,其中S是x后继状态的SG函数值的集合,一般情况下我们会开一个S[]数组,其中它的下标表示在当前for循环i下,取过一次之后后继状态的SG函数值。可以通过将这些后继状态得到的SG函数值全部标记,然后后续遍历S[]数组,找出最小的未被标记的值就作为找到的mex(S).
什么是x的后继状态? 例如取石子,每次有多种取法,取完之后的状态就是x的后继状态,因此有多个后继状态

例题1: 作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int N =1005;
int m,n;
int SG[N],f[N],S[N];
void ff()
{for(int i=1;i<N;i++){f[i]=pow(2,i-1);//f[i]中存取的每次拿牌可用的取法}
}
void GETSG(int n)//打表
{memset(SG,0,sizeof(SG));for(int i=1;i<=n;i++){memset(S,0,sizeof(SG));for(int j=1;f[j]<=i&&j<=20;j++)//该题最多有20种取法{S[SG[i-f[j]]]=1;//S为标记,将所有后继状态的SG函数值都标记了,即知道了后继状态是必败还是必胜。
//如果后继状态全是必胜点,则当前是必败点。如果后继状态有必败点,则当前是必胜点(因为你足够聪明让对手面临必败)}for(int j=0;;j++)//然后遍历S数组,找到第一个没有被标记的值,就是不存在于mex的值,即为该轮循环SG[i]的值{if(!S[j]){SG[i]=j;break;}}}
}
int main()
{int a;ff();while(scanf("%d",&a)!=EOF){GETSG(a);if(SG[a])printf("Kiki\n");elseprintf("Cici\n");}
}

2.一般题目均是先找到一必败点,然后逐渐往前推,也是打表
**例题:**输入一个数字字符串,两人轮流执行两个操作中的任意一个操作,最后不能执行操作的人败
(1) 将任意一位(个十百…)上面的数减小
(2) 将任意一个0后面的数删除(包括0)
由题意可知1是必败点,则从1开始往后推打表,SG[x],x指的就是该字符串转为的数字

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e6+7;
int SG[maxn];
int getlen(int x)
{int len=0;while(x){len++;x/=10;}return len;
}
void getSG(int n)
{int len=getlen(n);//计算n的位数int k=1;for(int i=1;i<=len;i++){int v=n;int t=(n/k)%10;//计算当前位的数字距离9的大小,看执行接下来的几次循环 for(int j=t+1;j<=9;j++){v=v+k;//将每一位上面的数字增加,然后SG函数值都为1,均是必胜点SG[v]=1;}k*=10;}//必败点的任意一位数字增加一点,都是必胜点 这两个for循环从个位开始,然后增加至9为止,依次十位,百位..... k=1;while(len<6){n*=10;for(int i=0;i<k;i++)SG[n+i]=1;//必败点在后面有0后,0后面加任意的数字都是必胜点 k*=10;len++;}
}
int main()
{SG[0]=1;for(int i=1;i<1000000;i++){if(!SG[i])//已知1是必败点,则从1开始找必胜点,能到达1的点都是必胜点,那么我们可以从必败点1开始推出所有必胜点 有两种情况①必败点任意1位数字增加都是必胜点 getSG(i);//②在必败点后面补0,0后面加任意数字都是必胜点 }char s[15];while(~scanf("%s",s)){int k=atoi(s);if(SG[k])printf("Yes\n");elseprintf("No\n");}return 0;
}

3.有的通过递归,但还是先找必败点
有多个容器,每个容器都有一定的容量和原有石子数,现在两个人轮流在任意容器放石子,规定每次放的数量不能超过原有石子数的平方。问先手胜还是先手败

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int SG(int n,int m)
{/*先找必败点,对一个容器分析,设容量为n,已有m石子,我们找一点x(两人轮流放石子过程中容器中石子量)当x,n满足  1.x+x*x<n  2.x+1+(x+1)*(x+1)>=n 这两个条件时x点为必败点因为你一下子没法装满,而对手下次必定装满。 即对一个容器能使对手面临到x点自己就赢了。此时最大的x可通过计算直接得出当m>x时,则先手就可以一下装完,先手必胜当m==x时,则先手面临必败点,先手必败当m<x时, 则先手就想让对手面临x点     若先手能一下子装的石子量可以到达x点,则先手必胜若先手能一下子装的石子量不能到达x点,则先手必败因此 当m<x时,先手的胜负就转换为在容量为x初始石子量为m的容器中的胜负,即SG(n,m) = SG(x,m)*/ int x=sqrt(n)+1;while(x+x*x>=n){x--;}if(m>x)return n-m;else if(m==x)return 0; else return SG(x,m);
}
int main()
{int n,a,b,sum,k=1;while(~scanf("%d",&n)){sum=0;if(n==0)break;for(int i=1;i<=n;i++){scanf("%d%d",&a,&b);sum^=SG(a,b);}printf("Case %d:\n",k);k++;if(sum)printf("Yes\n");elseprintf("No\n");}return 0;
}

洛谷p1290 欧几里得的游戏
欧几里德的两个后代Stan和Ollie正在玩一种数字游戏,这个游戏是他们的祖先欧几里德发明的。给定两个正整数M和N,从Stan开始,从其中较大的一个数,减去较小的数的正整数倍,当然,得到的数不能小于0。然后是Ollie,对刚才得到的数,和M,N中较小的那个数,再进行同样的操作……直到一个人得到了0,他就取得了胜利。下面是他们用(25,7)两个数游戏的过程:
Start:25 7
Stan:11 7
Ollie:4 7
Stan:4 3
Ollie:1 3
Stan:1 0
Stan赢得了游戏的胜利。
现在,假设他们完美地操作,谁会取得胜利呢?

题解:同样先找必败点,可知(3 1)局面是必胜点,那么(4 3)局面是必败点,那么(4 7)局面是必胜点,(11 7)局面是必败点,原因是对于两个数,一个数除以另一个数为1,那么每次操作都是固定的:(大的数-小的数) 因此对应局面也是相反的
对于两个数,一个数除以另一个数大于1的,仔细观察其后继局面 如(25 7),其后记局面可以为 (18 7) (11 7) 也可以为(7 4)
可知(11 7)与(7,4)必定是两个相反的局面,因此SG函数值必定有一个为0 ,所以(25 7)这个局面的SG函数值必定大于0,即一定是必胜点,因此对于两个数一个数除以另一个数大于1的,此局面就是必胜点

代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
bool SG(int n,int m)
{if(m==0)return false;if(n/m==1)return !SG(m,n%m);//如果大的数除以小的数==1,说明只有一种操作即大的数-小的数elsereturn true;//如果n/m>1,就是必胜局面
}
int main()
{int T,n,m;scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);if(SG(max(n,m),min(n,m)))printf("Stan wins\n");elseprintf("Ollie wins\n");}return 0;
}

SG函数求解 NIM游戏先手必胜必败问题相关推荐

  1. 博弈论与SG函数(Nim游戏)

    博弈论与SG函数(Nim游戏) 目录 博弈论与SG函数(Nim游戏) 游戏状态 状态图(SG图) Nim 游戏 Nim 和 SG函数 Grundy数字 组合博弈游戏 Grundy 游戏 例题 在本篇, ...

  2. NIM博弈+SG函数求解

    ICG 给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负. 这个游戏可以认为是所有 Impartial Combinatorial Games 的 ...

  3. HDU2176 【 Nim博弈】 SG函数求解

    取(m堆)石子游戏 m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆 ...

  4. sg函数和nim问题

    "Sprague-Grundy函数" 我们将面对更多与Nim游戏有关的变种,还会看到Nim游戏的a1^a2^...^an这个值更广泛的意义. 上面的文章里我们仔细研究了Nim游戏, ...

  5. nim游戏(判断必胜还是必败,必胜该怎么取)

    对于n堆石子,某个人没有石子可取为输,接下来的必胜态和必败态是对于第一个人来说的. 首先是必胜态和必败态,如果不管第一个怎么取都不能胜,那么为必败态,否则为必胜态. 如果对于n堆石子数取异或值为0,则 ...

  6. nim游戏的必胜策略

    假设有n堆石子,每堆石子的个数分别如下 a1, a2, a3, ... an 定义nim-sum为a1^a2^a3...an  可以证明 1. 若a1^a2^a3...an != 0 则经过一次合法的 ...

  7. 博弈论 - 状态图-推导必胜必败态

    #include <bits/stdc++.h> using namespace std;int w[105][105];void getw(){w[1][1]=0;for(int k=3 ...

  8. 【acm 博弈论 】 之 Nim游戏与sg函数

    文章目录 前言 巴什博弈 威佐夫博弈 Nim游戏 Nim游戏与sg函数 题目 题意 样例 思路 代码 前言 从今天开始复习和整理下acm的部分模块,从博弈论开始. 著名的"取石子" ...

  9. 【学习笔记】NIM游戏与SG函数初探

    公平组合游戏ICG 游戏条件介绍: 由两名玩家交替行动 游戏进程的任意时刻,可以执行的合法行动与轮到的玩家无关 若当前玩家无法行动,则判负 则称该组合游戏为公平组合游戏.如NIM游戏等即是经典的公平组 ...

最新文章

  1. python定义一个汽车类_汽车类Python程序
  2. 论坛答疑SQL(二)
  3. docker入门及安装
  4. optee中关于异常向量表、中断等的深入思考
  5. 原生html冻结表头,CSS如何实现表头冻结效果
  6. Java面向对象——基础3 其他关键字
  7. selenium 表单提交结果_了解 Selenium 定位方式
  8. 毕啸南专栏 | 对话王小川:搜狗不是谁的“变量”,是行业主要玩家
  9. wubi安裝ubuntukylin 14.04过程以及基本配置
  10. python怎么批量下载图片_批量下载网页图片(python)
  11. 解决XP精简版(无IIS的XP系统)安装IIS服务器的问题
  12. 计算机房设计规范2008,电子计算机房设计规范.doc
  13. 发现一本数学好书——重温微积分
  14. 苹果怎么锁定计算机,苹果电脑如何锁定屏幕-mac锁定屏幕教程 - 河东软件园
  15. 硬盘服务器与硬盘阵列,磁盘阵列和硬盘的区别是什么
  16. Pandas常见筛选数据的五种方法其一逻辑筛选。看见必懂,懂者必会,会者必加分
  17. 2021年职业病防治法宣传周宣传资料
  18. 【送书活动第一话】:送书伊始
  19. k8s部署nginx
  20. Unirech:阿里云国际版怎么获得免费试用的机会以及注册流程

热门文章

  1. Sql server索引优化
  2. idea开发SSM框架航院车辆出入收费管理系统停车收费系统 (javaweb-php-asp.netC#-j2ee-springboot)功能有按小时自动计算费用-统计报表
  3. 【jsoup】解析一个body片断
  4. 小白兔写话_可爱的小白兔看图写话二年级下册答案
  5. 电脑上不了网了......
  6. 企业针对嵌入式开发源代码及电路图纸如何防泄密?
  7. Qt 桌面悬浮画图软件--电子白板
  8. 线性回归模型分析学生成绩
  9. j2se培训第一天内容
  10. Linux防火墙之iptables