博弈
继上一篇https://www.cnblogs.com/yoshinow2001/p/14608742.html
http://poj.org/problem?id=1067威佐夫博弈,注意poj不能用万能头…
实现的时候直接用(k=a/varphi)来求,同时如果原本的(a)已经是一个出现在奇异局中的一个数,那(a=[kvarphi])这么做会让(a)变得偏小,所以我们同时检验(k)和(k+1)是否是可行的
$ $
https://www.luogu.com.cn/problem/P4279
反Nim游戏,分类讨论
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=2147
kiki的博弈(
(n imes m)的棋盘,开始在右上角,每次可以向左/向下/向左下走一步,不能操作的输
(n^2)地暴力求SG函数,注意这里有点卡空间,int开不下改成short
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=1848
Nim变形,每次取走Fibonacci数列中任一项个数的石子
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=4388
Nim变形,每次取(k)个同时保证(noplus k<n),并添加一个(noplus k)
$ $
写在博客园:https://www.cnblogs.com/yoshinow2001/p/14156886.html
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=2149
巴什博奕取石子改成拍卖,要求输出第一次的方案
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=3980
破环成链,(n)个珠子的项链,两个人轮流选一段连续的(m)个没染色的珠子给它染色,不能操作的输
- 破环成链:第一个人染完之后环就变成了一条链,以及特判一下第一个人染不了的情况
- 变成一条链的问题就可以跑SG了,我们说SG函数的异或和也是SG函数,考虑一条长为(n)的链,可以转化为长为(i)和(n-m-i)的子问题(染了一段长为(m)的链),子问题的状态对应着DAG中的后继结点,他们SG函数的异或和就对应着这个状态的SG
- 类似记忆化搜索一样跑
inline int calc(int n,int m){
if(SG[n][m]!=-1)return SG[n][m];
if(n<m)return (SG[n][m]=0);
bool tag[N];//把数组开在函数里面,因为是递归调用,算mex的时候如果重复用tag就出问题了…
memset(tag,0,sizeof(tag));
rep(i,0,n-m)tag[calc(i,m)^calc(n-m-i,m)]=1;
int j=0;
for(;tag[j];j++);
return SG[n][m]=j;
}
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=3595
Every SG
单个游戏:两堆石子,个数(n,m(n,mleq 10^3)),每次可以从一堆中取走另一堆的倍数个石子
现在是(N)个游戏同时进行操作,能操作的一定要操作。最后没有石子可移动者算输。
拿到题还是先考虑单个游戏的情况,直接(SG[n][m])暴力转移,带个记忆化(O(n^2))解决
考虑(N)个游戏同时进行的情况:首先明确,对若干个子游戏,似乎并不是只要有一个子游戏先手有必胜策略整个游戏就有必胜策略:比如说如果先手有必胜策略的那个游戏最多只能进行几轮,但是其他那些后手有必胜策略的子游戏至少能进行十几轮,那先手还是输。
基于这个例子接着考虑,对于你手上有必胜策略的子游戏,你希望他能进行地尽量久,同样对于对面手上有必胜策略的游戏,你希望尽早结束它。两个人都采取最优策略,所以一个子游戏进行的次数也是确定的。而(N)个游戏最终谁胜利也就只取决于进行轮次最多的那个游戏。
于是跑完单个SG之后判一下是必胜态还是必败态,连着处理出这个状态会进行的次数:
记(t[n][m])为这个状态会进行的轮次
- 如果当前是必胜态,那后继状态一定有必败态,我们从必败态里找(t)最大的那个+1
- 如果当前是必败态,要么(t[n][m]=0),要么一定有一个必胜的后继状态,从那些后继中找最小的+1
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=5754
【划重点】整博弈论四合一呢(
大概就是国际象棋,只能往右下方这个大方向走
-
王:向下,向右,向右下
考虑对于到达([i][j])就算胜利的子问题,(SG[i][j]=mex{SG[i-1][j],SG[i][j-1],SG[i-1][j-1]})
-
车:横着走或者竖着走
(O(n^3))打表,找规律发现(n=m)的时候先手必败,否则先手必胜
想一想也对,首先胜负情况只和之后要走的((Delta x,Delta y))有关,如果两个相等的话,不管先手怎么走,后手反着走就行(经典的“学对面走”的操作),否则的话先手在第一次操作一定可以把当前局面扭转成(Delta x=Delta y)的情形,相当于一次操作反转先后手,之后就变成后手先走的子问题,先手有必胜策略- 嗯?怎么这么熟悉
- 这不就是(n=2)的Nim么…
-
马:和中国象棋一样走日字格,有可能出现“平局”的情况
【划重点】似乎没法简单地用上面的做法来做类似的SG函数,不过…
- 如果没办法胜利,平局总比输了好
- 如果有办法胜利当然是要胜利
假设一共走了(a)次((1,2))和(b)次((2,1)),当前位置是((1+a+2b,1+b+2a))
- 如果(a=b)那么显然在对角线上的一些位置,对于这些位置后手有必胜策略:不管先手走哪一个,后手反着走就行
- 如果(a,b)相差为1,那其实就是偏离上一种情况一点,这些目标位置先手有必胜策略:第一步往一个正确的方向走,后面就跟后手反着走
- 否则如果相差超过1,那就意味着走第一种和走第二种的次数一定要超过2,但是这是不可能的,因为一旦出现(|a-b|= 1)的情况,另一个人就会把它扭转回(a-b=0),最后就会变成平局
-
皇后:向下、向右、向右下任意走
经典的移动皇后”/威佐夫博弈
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=3032
Nim变形,每次决策多了一个把一堆拆成两堆的操作,一样先暴力跑SG函数找规律x
证明先欠着(
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=5011
Nim改版,每次取完石子之后可以选择把当前这堆(x)拆成两个堆
暴力跑SG,发现跑出来的结果和普通Nim一样
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=1730
其实还是Nim的变形,一开始读错题以为是多行同时进行
WA了好几发发现是互相独立的,那似乎就好办许多,先考虑每个子问题的SG再异或起来
对于一行来说,如果一开始两个棋子相差为1,那后手有必胜策略,反之先手有必胜策略,证明很容易,只要能怼就一直怼过去(
考虑如何记录SG函数,相比普通的博弈这里有个限制:先手只能动黑棋,后手只能动白棋。记录棋子的位置似乎…有点难办
但是我们前面发现了胜负局是和棋子的距离相关的,记录距离:(SG[1]=0,SG[x]=x-1),然后就做完了
说是Nim的变形…嗯,这不就相当于取石子么(
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=1525
是3595那个Every SG里套娃的游戏,不过这里只要单个游戏,以及…没给数据范围…我一开始写(O(n^2))的SG又是MLE又是TLE…仔细想一下,这题只有一堆的问题而不是多堆的,为什么非要求SG呢…其实只要知道每个局面的胜负情况就行了…
inline int calc(int a,int b){
if(a>b)swap(a,b);
if(a==0)return 0;
if(a==1||b==1)return 1;
if(a==b)return 1;
if(b/a==1)return 1-calc(a,b-a);
//a<=b
return 1;
/*int t=b%a;
int r=min(calc(a,t),calc(a,t+a));
return (1-r);
*/
}
于是好像就做完了…特判掉一些边界情况,对于(lfloor b/a floor =1)的局面,后继状态只有((a,b-a))一种,而剩下的就是(lfloor b/a floor geq 2)的局面,类似更相减损改成辗转相除那样,它的后继状态可以是((a,bmod a),(a,bmod a+a),dots,(a,bmod a+ka))这些,如果一个一个求的话和暴力求SG又一样了…但是我们并不需要这么多信息,因为((a,bmod a))和((a,bmod a+a))这两个状态的胜负局面一定是相反的…也就是说一定有一个必败态,那只要选必败态当前局面就一定是必胜局面了…做完了
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=4387
【还是不太会】
一开始又读错题意了…(1 imes n)的棋盘,左边从左到右编号为(1,2.dots,k)的(k)个白棋,右边从右到左同样编号(k)个黑棋,白棋只能向右黑棋只能向左, 双方轮流选一个自己的棋子,走到右边/左边最靠近自己的空位,最先不能走(没有棋子可以移动了)的赢,(2k<n),白棋先走
“最靠近自己的空位”很关键,虽然能跳着走但是并不能随便跳
- 还是从小情况入手,如果(k=1),相当于两个棋子每次往右走一格,直到碰头,如果(n)是偶数的话,碰头那一步白棋会一下跳两格,黑棋走一格,先手获胜,反之后手获胜。
- 对于(k>1)的情况,如果说恰好有(n=2k+1),也就是说只有中间一个空位
- 先手第一步一定会占据中间的位置,之后的所有移动类似“交换位置”,后手占据先手上一轮留出的空位,先手再占据后手的空位。
- 进一步也就是说在后手要走的时候先手那边会有一个空,同样先手走的时候后手那边有一个空,最后局面会变成先手的(k-1)个都到了右侧,后手的(k)个都到了左侧,接着右侧有一个空位,先手用中间的棋子补上
- 对于更一般的情况,先手依然有必胜策略,不过要先走(1)号棋子
$ $
https://codeforces.com/problemset/problem/832/A
看懂题意就是有手就行…
$ $
http://acm.hdu.edu.cn/showproblem.php?pid=3537
翻硬币游戏,最右边必须是正到反,一次可以选择翻不连续的一到三个的版本
有一些奇妙的结论…