终于我也开始学博弈了,说了几个月,现在才学。学多点套路,不深学。(~~)

参考刘汝佳蓝书p132

nim游戏:

假设是两维的取石子游戏,每次可以在任意一堆拿任意数量个(至少一根,因为这样游戏的状态集有限,所以如果可以不拿的,那应该不是nim游戏了,大家都不拿,出现了bug),同时规定没有办法拿的那个人输。

那么记录状态win[i][j]表示第一堆是i个,第二堆是j个的时候,是否必胜。则win[0][0] = false

win[0][1--m] = true因为可以拿走全部,使得对手走去win[0][0]的必败状态。同理win[1---n][0] = true

那么,定义:

①、一个状态是必败态当且仅当它的所有后继(也就是所有取石子方案)都是必胜态。  必败态是取什么都败

②、一个状态是必胜态当且仅当它至少有一个后继是必败态。   注意,必胜态不是任取都胜,是通过某种策略取了,然后胜利。

③、没有后继的状态是必败态(因为这里我们假设不能走就是输)

有了上面的基础,

学习了Chomp!游戏

有一个n*m的矩阵,每次可以拿走(x, y)及其右上方的所有格子,谁拿到(1, 1)这个格子的输。

// 嗯所以win[1][1] = false, win[1][2--m] = true, win[2---n][1] = true,然后我不会推了,

这里用的是反证法,假设先手必败。但是我发现一个技巧就是,你只能假设他必败,因为定义1,必败态的所有后继都是必胜。如果你假设它必胜,那么只需要找到一个后继是必败就能证明,但这往往很难找。所以要假设它是必败的。

那么如果先手必败,它取哪一个都是败,设是(n, m),这个时候后手必胜,它通过某种策略取到了必胜局面。那么事实上,先手在第一次取得时候就可以和后手这次取得一模一样,抢先进入必胜局面,这和假设矛盾。所以只有n==1&&m==1是输,其他赢。

变种:约数游戏。

有1--n个数字,两人轮流选择一个数,并且把这个数和它的约数全部删去,擦去最后一个的人赢。

假设先手必败,就是它任取哪一个都败,假设我取了1,然后后手通过某种策略,擦去了x,使得进去必胜局面,那么其实这个局面先手可以抢先进入,因为1的所有数字的约数。所以先手永远必胜。

nyoj 23 取石子(一)

http://acm.nyist.net/JudgeOnline/problem.php?pid=23

设f[0]是必败态,那么f[1--m]是必胜态,f[m + 1]的后继只有f[1---m],也就是留了一个必胜的后继给对手,所以f[m  + 1]是必败态。一路算下去,只要是m + 1的倍数的,都是必败态。

原来这是巴什博奕(Bash Game):

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset> void work() {
int n, m;
scanf("%d%d", &n, &m);
if (n <= m) {
cout << "Win" << endl;
return;
}
int base = m + ;
n %= base;
if (n == ) {
cout << "Lose" << endl;
} else cout << "Win" << endl;
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
int t;
scanf("%d", &t);
while (t--) work();
return ;
}

Nim和

假设是取三堆石子,每堆石子至少取一个,可以取完,这样的游戏怎么快速解答,因为数字很大。

设f(x1, x2, x3)表示三堆石子现在剩余个数的状态。

L.Bouton定理:状态f(x1, x2, x3)是必败态的条件是:当且仅当x1 ^ x2 ^ x3 == 0,称为Nim sum

这是单个游戏的结论,就是这个游戏就是给你三堆石子来玩。

但是组合游戏:是给你很多个子游戏,每个子游戏就是三堆石子来玩。组合游戏都是很复杂的。注意你不能直接把x个子游戏里面的石头的值全部异或起来,然后判断nim和,这是不对的,因为每个游戏不互相影响。

那么怎么判断整个组合游戏的局面呢?

sg函数,怎么说呢,就是它的后继集合,第一个没有出现的非0整数。其实整个博弈过程就是一颗树。

从状态f(x, y)--->【f(a, b), f(c, d), f(x, y)】都是它的后继,就是能直接到达的状态。每个状态又有后继。那么可以知道,没有后继的状态是必败的,因为你没得取石子了。记为0,那么它的父状态是必胜的,记为1,递归上去即可。

一开始以为sg函数不是0就是1,其实是错的,因为一个状态有很多兄弟状态,如果兄弟状态的值是0、1、那么父状态就是2了。

可以知道,一个状态为必败态的充要条件是sg值等于0.等于0说明它的后继没有0,它的后继没有0说明所有后继状态都是必胜态。

所以父状态是必败的。

sg定理:组合游戏和的sg函数等于各个子游戏sg函数的nim和。(nim和其实就是全部异或起来)

组合游戏和的sg函数,也是一样,必败态的充要条件是sg值等于0。

那么其实上面说的判断nim游戏的输赢,其实就是sg定理的直接应用。可以看出若干个一堆的nim游戏。

假设一个nim游戏是三堆石子x, y, z,那么sg(x) = x的,是必胜的,因为一次全拿就是胜了。

所以nim和就是sg(x) ^ sg(y) ^ sg(z),也即是x ^ y ^ z

---------------------------------------------------------------------------------------------------------------------------------

nyoj 135 取石子(二)

http://acm.nyist.net/JudgeOnline/problem.php?pid=135

这题是n个的bash游戏。

算出整个组合游戏的sg即可,单个游戏的sg值是n % (m + 1),可以先看看sg值找下规律。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset> const int maxn = ;
int sg[maxn], vis[maxn];
LL calc(LL n, LL m) {
return n % (m + );
}
void work() {
int n;
cin >> n;
LL res = ;
for (int i = ; i <= n; ++i) {
LL x, y;
cin >> x >> y;
if (x == || y == ) continue;
res ^= calc(x, y);
}
if (res) {
cout << "Win" << endl;
} else cout << "Lose" << endl;
// sg[0] = 0;
// for (int i = 1; i <= n; ++i) {
// memset(vis, 0, sizeof vis);
// for (int j = 1; j <= min(i, m); ++j) vis[sg[i - j]] = 1;
// for (int j = 0; ; ++j) {
// if (!vis[j]) {
// sg[i] = j;
// break;
// }
// }
// printf("%d ", sg[i]);
// }
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
int t;
scanf("%d", &t);
while (t--) work();
return ;
}

注意,sg值是4说明必胜,sg值是1也是说明必胜,但是两者是不等价的。

SG函数真的很好用,打表找下规律就马上出来了。

A - The game of Osho Gym - 101147A 

http://codeforces.com/gym/101147/problem/A

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset> const int maxn = ;
int sg[maxn * maxn];
bool vis[maxn * maxn];
int calc(int n, int k) {
if (k > n) return n & ;
if (k & ) return n & ;
else {
int dis = k - + ;
if (n > dis) n %= dis;
if (n <= k - ) {
return n & ;
} else {
n -= k - ;
return n % ;
}
}
}
void calcSg() {
int n, k;
scanf("%d%d", &n, &k);
sg[] = ;
for (int i = ; i <= n; ++i) {
memset(vis, false, sizeof vis);
int t = ;
for (t; t <= i; t *= k) {
vis[sg[i - t]] = true;
if (k == ) break;
}
for (int j = ; ; ++j) {
if (!vis[j]) {
sg[i] = j;
break;
}
}
}
for (int i = ; i <= n; ++i) {
printf("%d ", sg[i]);
}
printf("\n");
cout << endl;
cout << calc(n, k) << endl;
cout << sg[n] << endl;
} void work() {
int has;
int res = ;
scanf("%d", &has);
while (has--) {
int n, k;
scanf("%d%d", &k, &n);
res ^= calc(n, k);
// cout << n << " " << k << " " << calc(n, k) << " fff" << endl;
}
if (res) {
printf("1\n");
} else printf("2\n");
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
// calcSg();
freopen("powers.in", "r", stdin);
int t;
scanf("%d", &t);
while (t--)
work();
return ;
}

跪了,跪了一个晚上,做NYOJ 161 取石子 (四)

http://acm.nyist.net/JudgeOnline/problem.php?pid=161

想到了怎么打sg表,也打出来了,可是还是找不到规律。

然后居然发现是威佐夫博奕,差值 * (1.618) == mi就是必败态。

sg还是挺好打的,像以前一样枚举一维中取1、2、3、....、4。在第二维中取1、2、3、4、.....

两维同时取1、2、3、4...5

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset> const int maxn = + ;
int sg[maxn][maxn];
bool vis[maxn * maxn];
int a, b;
void calcSg() {
int n, m;
cin >> n >> m;
for (int i = ; i <= n; ++i) {
sg[i][] = ^ i;
}
for (int i = ; i <= m; ++i) {
sg[][i] = ^ i;
}
for (int i = ; i <= n; ++i) {
for (int j = ; j <= m; ++j) {
// sg[i][j] = i ^ j;
memset(vis, , sizeof vis);
for (int t = ; t <= min(i, j); ++t) {
vis[sg[i - t][j - t]] = true;
}
for (int t = ; t <= i; ++t) {
vis[sg[i - t][j]] = true;
}
for (int t = ; t <= j; ++t) {
vis[sg[i][j - t]] = true;
}
for (int t = ; ; ++t) {
if (!vis[t]) {
sg[i][j] = t;
break;
}
}
}
}
for (int i = ; i <= n; ++i) {
for (int j = ; j <= m; ++j) {
printf("%4d ", sg[i][j]);
// cout << i << " " << j << " " << sg[i][j] << endl;
}
cout << endl;
}
}
void work() {
const double res = (sqrt(5.0) + ) / ;
int mi = min(a, b);
int mx = max(a, b);
int dis = mx - mi;
if ((int)(dis * res) == mi) {
cout << "" << endl;
} else cout << "" << endl;
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
// calcSg();
while (cin >> a >> b && (a + b)) work();
return ;
}

博弈论 && 题目的更多相关文章

  1. 博弈论题目总结(二)——SG组合游戏及变形

    SG函数 为了更一般化博弈问题,我们引入SG函数 SG函数有如下性质: 1.如果某个状态SG函数值为0,则它后继的每个状态SG函数值都不为0 2.如果某个状态SG函数值不为0,则它至少存在一个后继的状 ...

  2. 博弈论经典算法(一)——对抗搜索与Alpha-Beta剪枝

    前言 在一些复杂的博弈论题目中,每一轮操作都可能有许多决策,于是就会形成一棵庞大的博弈树. 而有一些博弈论题没有什么规律,针对这样的问题,我们就需要用一些十分玄学的算法. 例如对抗搜索. 对抗搜索简介 ...

  3. 20181228 模拟赛 T3 字符串游戏 strGame 博弈论 字符串

    3  字符串游戏(strGame.c/cpp/pas) 3.1  题目描述 pure 和 dirty 决定玩 T 局游戏.对于每一局游戏,有n个字符串,并且每一局游戏由K轮组成.具体规则如下:在每一轮 ...

  4. 博弈论基础之sg函数与nim

    在算法竞赛中,博弈论题目往往是以icg.通俗的说就是两人交替操作,每步都各自合法,合法性与选手无关,只与游戏有关.往往我们需要求解在某一个游戏或几个游戏中的某个状态下,先手或后手谁会胜利的问题.就比如 ...

  5. POJ 博弈论

    poj1704 Georgia and Bob 题目链接:http://poj.org/problem?id=1704 题意:如图所示,两个人在玩一个游戏,排成直线的格子上有n个棋子,两人依次将棋子向 ...

  6. ACM 取石子(七)

    取石子(七) 时间限制:1000 ms  |  内存限制:65535 KB 难度:1   描述 Yougth和Hrdv玩一个游戏,拿出n个石子摆成一圈,Yougth和Hrdv分别从其中取石子,谁先取完 ...

  7. USACO Section 3.3: A Game

    第一次碰到博弈论题目,是很棘手,博弈论题目要考虑全局最优的解法,我第一次用了局部最优的,而且vector也没pop_front()操作.后来看了网上的用dp的方法解的. 博弈论的题目基本都得用dp法子 ...

  8. 计蒜客 取数游戏(dp)

    有如下一个双人游戏:N个正整数的序列放在一个游戏平台上,两人轮流从序列的两端取数,每次有数字被一个玩家取走后,这个数字被从序列中去掉并累加到取走该数的玩家的得分中,当数取尽时,游戏结束.以最终得分多者 ...

  9. POJ 1082 Calendar Game(找规律博弈)

    传送门 以下复制自此处:http://www.xuebuyuan.com/2028180.html 博弈论题目可以用寻找必败状态的方法解决. 第一个必败状态是2001.11.04.由此可以推出其他任何 ...

随机推荐

  1. java对象的初始化过程和创建对象的几种方式

    1.加载父类,加载父类的静态属性和静态代码块 2.加载子类,加载子类的静态属性和静态代码块 3.初始化父类中的非静态属性并赋初值,执行父类非静态代码块,执行父类构造. 4.初始化子类中的非静态属性并赋 ...

  2. C++中的宏和const

    在C语言中使用const来定义一个变量,可以通过变量类型的指针形式来进行修改,而C++中增强了这种表现形式,使得即使通过类型变量指针也不能对变量进行修改. 在C++中const和宏是有区别的. con ...

  3. 网络编程学习笔记-MAC地址和IP地址的关系

    简单地说:ip地址是服务商给你的,mac地址是你的网卡物理地址. 一.IP地址 对于IP地址,相信大家都很熟悉,即指使用TCP/IP协议指定给主机的32位地址.IP地址由用点分隔开的4个8八位组构成, ...

  4. cm 安装

    为Cloudera Manager建立数据库:/usr/share/cmf/schema/scm_prepare_database.sh mysql -h[mysql数据库的主机名] -P63751 ...

  5. vim 模式下的几个快捷用法

    1.ctrl + v  (-- VISUAL BLOCK --) 选中块模式,y 复制,d 剪切,p 粘贴,Esc退出模式 2.Shift + v  (-- VISUAL LINE -- ) 快速行选 ...

  6. tyvj1015公路乘车——DP

    题目:http://www.joyoi.cn/problem/tyvj-1015 代码如下: #include<iostream> #include<cstdio> using ...

  7. The Tomcat server configuration at \Servers\Tomcat v8.0 Server at localhost-config is missing. Check the server for erro

    解决方案 1.选择Eclipse工具栏中的Windows→Perferences 2.remove已经创建的server 3.选择Add重新添加,选择create anew local server ...

  8. selenium 点击页面链接测试

    点击页面链接测试 http://www.51testing.com/html/21/n-862721.html 需求:现在有一个网站的页面,我希望用python自动化的测试点击这个页面上所有的在本窗口 ...

  9. [poj2019]Cornfields(二维RMQ)

    题意:给你一个n*n的矩阵,让你从中圈定一个小矩阵,其大小为b*b,有q个询问,每次询问告诉你小矩阵的左上角,求小矩阵内的最大值和最小值的差. 解题关键:二维st表模板题. 预处理复杂度:$O({n^ ...

  10. Linux 错误集锦

    1. CentOS 7 运行yum时出现/var/run/yum.pid已被锁定,PID为xxxx的另一个程序正在运行的问题解决 解决办法: rm -f /var/run/yum.pid,删除文件后再 ...