生活日用算法之NIM博弈

故事还是得从笔者某次去ktv说起。席间见到某汉子拿出15个色子,分成3,5,7三组,开始和妹子按照以下规则玩耍:

1、每次可以从任意一组中取任意个色子

2、取到最后一组的人赢

任意先后手,大多是某汉子一直赢,然后妹子一直喝,然后。。。

故事背景大概就是这样,回去后我尝试用枚举的方式去寻找必胜策略。大概可以等到以下形式的必胜组合2,2;3,3;4,4;5,5;1,2,3;1,4,5等,但转念一想如果玩的多了妹子也背下了必胜组合,我方就必须得实时更新自己脑海中的必胜组合,这不符合我们一个理科生的世界观。于是仔细百度了一下此类问题,发现原来早走定论,就是一个MIN博弈而已。

先来讲下异或运算的概念,简单来讲,设a不等于b,则a⊕b=1,a⊕a=0,b⊕b=0,b⊕a=1,文字表述如下:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。

接下来我们回到问题本身,如何在此游戏中保持必胜,简单来讲,对于取到最后一组的人赢的情况,我们只需保证,每次自己取完,使得各组的二进制异或运算结果为0即可。或者说,保证各组转化二进制后,各位数上的1的总量为偶数

简单证明如下:

1、首先当我方取掉最后一组取得胜利后,剩余情况符合上述情况,简称胜利态好了。

2、胜利态中的任意操作后,总会转化为非胜利态。总量减少,被移动的那一组一定有二进制1变0,那么这一位的1总量偶数变奇数。

3、由胜利态转换来的非胜利态,一定可以通过一次移动,再转换为胜利态:

因为无论对方移动那一堆,其移动后消失的二进制最大值,一定有另一堆具备。。否则异或运算不会为0。因为a与任意个0进行异或运算,结果一定为1此时,把对应堆最大值取掉,其余堆对应移动即可。

举例说明下,比如,现在有三堆为1+2;1+4;2+4这种情况,对方选择从第三堆中移除3个, 此时可以此变换转变为4-1,即110->11,可以发现变化的位置为第三位和第一位。我么只要找到4对应存在的位置,即2+4,二进制110,然后只要对他的第三位和第一位取反,变成011即可。

实际操作中,如果发现你先手总是赢,你给她摆一个胜利态让她先手,就可以继续套路了,比如5,9,12

为了增加趣味性,可以把规则变为取到最后一个的输,这时候问题就转化为了反NIM问题

此问题的必胜态与必输态如下:

1、如果剩余所有堆均为1,则根据堆总数为偶数为必胜态。

2、如果剩余堆的中只有一堆不为1,则各组的二进制异或运算结果必不为0,则此状态为必输态。

3、如果剩余堆中有多余一堆不为1,则各组的二进制异或运算结果为0为必胜态。

证明如下:

在对于两组以上,我方保持3状态,对方只能做出非3状态,我方可转换为3

只剩两组非1时,我方保持3状态,对方最终将只能转变为2状态。详解如下:

如果1的数量为偶数,非1堆保持相同,为奇数,非一堆保持一个比另一个多1。持续取两堆时,前者转变为偶数个1与2,2。。对方取2,2任意,则会变为必胜态3状态,后者终转变成2,3与奇数个1,此是对方取一个1,则我方取3->2编程上述状态,对方取3->2我方取1.其余取法对方会呈现必输态2

总结来讲,非1堆大于1时,保持与NIM博弈同种策略即可

玩法继续多样性,还可以规定每次最多只能去k个,此时的必胜态是把各堆转化为二进制,是取二进制1的总数mod k为0即可。证明略。

原文地址:https://www.cnblogs.com/fbw-gxy/p/10844129.html

时间: 2024-10-31 19:32:38

生活日用算法之NIM博弈的相关文章

生活日用算法——八皇后问题

八皇后问题也算是比较经典的回溯算法的经典案例.题干描述如下: 在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法 对此首先我们使用array[][]来构建一个棋盘,然后尝试落子,此时算法如下: /** * 寻找皇后节点 * @param row * @param size */ public static void findQueen(int row, int size) { // 如果皇后行已达到最后一行,结果数量自增,

博弈-Nim博弈

Nim博弈-属于组合游戏 条件: 1.有两名选手: 2.两名选手交替对游戏进行移动(move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动: 3.对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作.以前的任何操作.骰子的点数或者其它什么因素: 4.如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负.根据这个定义,很多日常的游戏并非ICG.例如象棋就不满足条件3,因为红方只能移动红子,黑方

POJ Cutting Game(Nim博弈+grundy数)

Description Urej loves to play various types of dull games. He usually asks other people to play with him. He says that playing those games can show his extraordinary wit. Recently Urej takes a great interest in a new game, and Erif Nezorf becomes th

ACM学习历程—HDU 3915 Game(Nim博弈 && xor高斯消元)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3915 题目大意是给了n个堆,然后去掉一些堆,使得先手变成必败局势. 首先这是个Nim博弈,必败局势是所有xor和为0. 那么自然变成了n个数里面取出一些数,使得xor和为0,求取法数. 首先由xor高斯消元得到一组向量基,但是这些向量基是无法表示0的. 所以要表示0,必须有若干0来表示,所以n-row就是消元结束后0的个数,那么2^(n-row)就是能组成0的种数. 对n==row特判一下. 代码:

UVA 11859 Division Game (Nim博弈)

题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=32746 题意:有一个n*m(1<=n,m<=50)矩阵,每个元素均为2~10000之间的正整数,两个游戏者轮流操作.每次可以选一行中的1个或者大于1的整数,把他们中的每个数都变成它的某个真因子,比如12可以边长1,2,3,4或者6,不能操作的输. 分析:考虑每个数包含的素因子个数(比如12=2*2*3包含3个素因子),则让一个数"变成它的素因子"

hdu 5011 (nim博弈模版)

//nim博弈 //有n堆石头,两人轮流每次从一堆中拿至少1,之多全部的石头,没有石头可拿为lose //判断先手是win还是lose # include <stdio.h> # include <algorithm> # include <string.h> using namespace std; int main() { int n,i; __int64 a,sum; while(~scanf("%d",&n)) { sum=0; fo

HDU 1849 Rabbit and Grass(nim博弈)

题目地址:HDU 1849 初次接触nim博弈,感觉好神奇的说...居然可以跟异或运算扯上关系....给人类的智商跪了...作为地球人我感到很自豪.. 具体证明什么的看这篇博客被.传送门 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #inclu

HDU 1907 Nim博弈变形

1.HDU 1907 2.题意:n堆糖,两人轮流,每次从任意一堆中至少取一个,最后取光者输. 3.总结:有点变形的Nim,还是不太明白,盗用一下学长的分析吧 传送门 分析:经典的Nim博弈的一点变形.设糖果数为1的叫孤独堆,糖果数大于1的叫充裕堆,设状态S0:a1^a2^..an!=0&&充裕堆=0,则先手必败(奇数个为1的堆,先手必败).S1:充裕堆=1,则先手必胜(若剩下的n-1个孤独堆个数为奇数个,那么将那个充裕堆全部拿掉,否则将那个充裕堆拿得只剩一个,这样的话先手必胜).T0:a1

UVA 1559 - Nim(博弈dp)

UVA 1559 - Nim 题目链接 题意:一开始有s个石子,2n个人轮流取石子,每个人有个最大能取数目,2n个人奇数一队,偶数一队,取到最后一个石子的队输,问谁赢 思路:记忆化搜索,每个人取的时候对应的后继状态如果有一个必败态,则该状态为必胜态,如果都是必胜态,则该状态为必败态 代码: #include <stdio.h> #include <string.h> int n, s, m[25], dp[25][10005]; int dfs(int now, int state