组合游戏的初步学习
2016-07-20 22:12
344 查看
组合游戏以前接触的比较少,现在学习一下,这种东西还是结合题目好理解点.
网上比较好的博客:
题目:hdu 1846 Brave Game
威佐夫博奕(Wythoff Game):
题目:poj 1067 取石子游戏
代码:
尼姆博奕(Nimm Game):
一般可以用SG函数求解
* 有些题目可以从终结点(必败点P)推出其他点的性质,一般可以发现规律。 *
例题:
1. hdu 1846 Brave Game
2. poj 1067 取石子游戏
SG函数:
下面是几道用到SG函数的简单题目
1. hdu 1846 Brave Game
2. hdu 1847 Good Luck in CET-4 Everybody!
3. hdu 1848 Fibonacci again and again
hdu 1848
题意:
Nim游戏,只不过每次从石堆里可以拿的数是菲波那切数。其它地方跟Nim游戏一样。
网上比较好的博客:
组合博弈 – 三大基本博弈
巴什博奕(Bash Game) :有一堆n个物品,两人轮流从堆中取物品,每次取 x 个 ( 1 ≤ x ≤ m)。最后取光者为胜。
题目:hdu 1846 Brave Game
威佐夫博奕(Wythoff Game):
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
题目:poj 1067 取石子游戏
代码:
#include<cstdio> #include<cmath> #include<algorithm> int main() { int a,b,k,a_k; while(~scanf("%d%d",&a,&b)){ k = abs(a-b); a = a < b? a : b; a_k = floor(k*(1.0 + sqrt(5.0))/2); printf("%d\n",a!=a_k); //输出为0,说明该点为必败点,1为必胜点 } return 0; }
尼姆博奕(Nimm Game):
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
一般可以用SG函数求解
必胜点和必败点的概念:
P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。 N点:必胜点,处于此情况下,双方操作均正确的情况下必胜。
必胜点和必败点的性质:
1、所有终结点是 必败点 P 。(我们以此为基本前提进行推理,换句话说,我们以此为假设) 2、从任何必胜点N 操作,至少有一种方式可以进入必败点 P。 3、无论如何操作,必败点P 都只能进入 必胜点 N。
* 有些题目可以从终结点(必败点P)推出其他点的性质,一般可以发现规律。 *
例题:
1. hdu 1846 Brave Game
2. poj 1067 取石子游戏
组合游戏 - SG函数和SG定理
Sprague-Grundy定理(SG定理):游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。而Bouton定理就是Sprague-Grundy定理在Nim游戏中的直接应用,因为单堆的Nim游戏 SG函数满足 SG(x) = x。不知道Nim游戏的请移步:这里
SG函数:
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。 对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。
下面是几道用到SG函数的简单题目
1. hdu 1846 Brave Game
2. hdu 1847 Good Luck in CET-4 Everybody!
3. hdu 1848 Fibonacci again and again
hdu 1848
题意:
Nim游戏,只不过每次从石堆里可以拿的数是菲波那切数。其它地方跟Nim游戏一样。
#include<bits/stdc++.h> using namespace std; const int N=1009; const int M=20; int f[M],sg ,vis ; void getSG(int n) { int i,j; memset(sg,0,sizeof(sg)); for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); for(int j=0;f[j]<=i&&j<M;j++)vis[sg[i-f[j]]]=1; for(j=0;;j++)if(!vis[j]){ sg[i]=j;break; } } } int main() { int n,m,p; f[0]=f[1]=1; for(int i=2;i<M;i++)f[i]=f[i-1]+f[i-2]; getSG(1000); while(~scanf("%d%d%d",&m,&n,&p)&&(n+m+p)){ if(sg[m]^sg ^sg[p])printf("Fibo\n"); else printf("Nacci\n"); } return 0; }
相关文章推荐
- 大型网站架构系列:负载均衡详解(3)
- java中volatile关键字的含义
- SQLite清空表并将自增列归零
- openwrt 恢 复 出厂设置
- 大型网站架构:负载均衡(2)
- ORACLE PL/SQL 实例精解之第一章 PL/SQL概念
- 【bzoj1031】【JSOI2007】【字符加密】【Cipher】【字符串】【后缀数组】
- 提示框样式说明android:windowFrame
- 已知先序、中序求后序;已知中序、后序求先序(C++)
- Win32调用大漠插件dm.dll教程
- 分金条
- 与组长交流
- JavaScript 利用原型和原型链实现对象继承
- 集训 Poj 1979
- GCD
- LeetCode - 171. Excel Sheet Column Number
- EXTJS4.0.7开发积累(3)
- pip
- 利用webStorm编写代码实现页面跳转功能
- NDK之Application.mk文件