one of Algorithm ---- 博弈论
原理:游戏都建立在双方都有最优策略的情况下
典型例题(level1)
特点:所走的步数是固定的 多选一
例1:取石子游戏之一
有两个游戏者:AA和BB,有nn颗石子。
约定:两人轮流取走石子,每次可取1,21,2或33颗。AA先取,取走最后一颗石子的人获胜。
问题:AA有没有必胜的策略?
分析:这是小学必备奥数题之一,我们可以很容易的知道,
当n为0,4,8,12……4的倍数时,A必定会输,因为不论A取多少,B只要和A共同取走4即可;当n不为0,4,8,12……时,A只需要将n取成4的倍数,这样就变成了B先取,B一定会输,所以A一定会赢。
!!!经过我们的分析发现,对这个游戏而言,0,4,8,12……这些状态是对于先手的必败状态,而其他状态是对于先手的必胜状态。
总结:
如果我们推广一下,每次不一定取1,2,3颗,而是取1∼m颗,那么我们就可以得到,
如果n%(m+1)=0,即为先手必败状态,否则为先手必胜状态。而这个游戏就是著名的巴什博弈(Bash Game)
题型总结
1、平等组合游戏
两人游戏。
两人轮流走步。
有一个状态集,而且通常是有限的。
有一个终止状态,到达终止状态后游戏结束。
游戏可以在有限的步数内结束。
规定好了哪些状态转移是合法的。
所有规定对于两人是一样的。
因此我们的例1提到的游戏即为一个平等组合游戏,但是我们生活中常见的棋类游戏,如象棋、围棋等,均不属于平等组合游戏,因为双方可以移动的棋子不同,不满足最后一个条件;而我们后续提到的游戏,以及博弈中的其他游戏,基本属于平等组合游戏
第二种题型
重要原理:
!!!特殊情况
如果仅仅是两堆石子,那么上述两个问题很好解决:
1〉当两堆石子数目相等的时候,当前局面为必败局面,否则为必胜局面,显然,两堆均为0颗是满足这个方法的;
2〉如果当前局面是必胜局面,那么从石子较多的那一堆里面取,使得两堆石子数相等,这样便转化到了必败局面。
大于两堆的情况:
1〉把所有堆的石子数目用二进制数表示出来,当全部这些数按位异或结果为0时当前局面为必败局面,否则为必胜局面;
2〉(定理0)一组自然数中必然存在一个数,它大于等于其它所有数按位异或的结果。因此在必胜局面下,因为所有数按位异或的结果是大于零的,那么通过一次取,将这个(大于其它所有数按位异或的结果的)数下降到其它所有数按位异或的结果,这时局面就变为必败局面了。
简单介绍 异或运算的性质及应用
概念
异或是一种基于二进制的位运算,用符号XOR或者 ^ 表示,其运算法则是对运算符两侧数的每一个二进制位,同值取0,异值取1。它与布尔运算的区别在于,当运算符两侧均为1时,布尔运算的结果为1,异或运算的结果为0。
#简单理解就是不进位加法,如1+1=0,,0+0=0,1+0=1。
应用
1. 交换两数的值
所有的程序教科书都会向初学者指出,要交换两个变量的值,必须要引入一个中间变量。但如果使用异或,就可以节约一个变量的存储空间: 设有A,B两个变量,存储的值分别为a,b,则以下三行表达式将互换他们的值 表达式 (值) :
A= A XOR B (a XOR b)
B= A XOR B (b XOR a XOR b = a)
A= A XOR B (a XOR b XOR a = b)
2. 找出重复数字
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现
一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空
间,能否设计一个算法实现?
解法一、显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去1+2+…+1000的和。
这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。
解法二、异或就没有这个问题,并且性能更好。
将所有的数全部异或,得到的结果与123…1000的结果进行异或,得到的结果就是重复数。
例一
0 的作用仅仅是改变后续操作的奇偶性,因此 {0}0 的个数 aa 可以对 2 取模。
接下来,我们先不考虑 0 的存在。
先手取了1 之后,后手必定取 {1}1 ,然后先后手依次是 {2,1,2,1,2,1\;
先手取了 22 之后,后手必定取 22,然后先后手依次是 {1,2,1,2,1,2}。
综上,观察到取的方式一定是 1,2 交替(除了开头)。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
long long a,b,c;
cin>>a>>b>>c;
//若bc缺少任意一个 都是后手赢
if((b!=0&&(a+(b-1>c))%2==0)//(a是奇数 和 b-1>c 两个条件必须同时满足)
||
(c!=0&&(a+(c-1>b))%2==0)) cout<<"First"<<'\n';
else cout<<"Second"<<'\n';
}
return 0;
}
二进制最低位为1时,就是奇数