博弈论--从 必胜点与必败点 到 SG 函数
一、必胜点与必败点
规则:当一方不能将游戏继续下去时,游戏结束,对方获胜。
必败点(P点) 前一个(previous player)选手将取胜的点称为必败点
必胜点(N点) 下一个(next player)选手将取胜的点称为必胜点
(1) 所有终结点是必败点(P点);
(2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点);
(3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点).
方法:
步骤1:将所有终结位置标记为必败点(P点);
步骤2: 将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;
步骤4: 如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2。
hdu2147
画出PN图,即可得结论。
二、Nim博弈
有若干堆石子,分别有N1,N2...Nm颗,2人轮流取子,每次可以任选一堆,取任意颗。最后无法再操作的人输。
解法:将所有石子数异或,N1 ^ N2 ^ ... Nm,若为0,则先手必败;不为0,则先手必胜。
hdu1850 Being a Good Boy in Spring Festival
求先手获胜,有多少种走法。
#include <iostream>
#include <cstdio>
using namespacestd;
const int maxn =100 +5;
int num[maxn];
int main()
{
int m,cnt,ans =0;
while (scanf("%d",&m) !=EOF && m) {
ans = 0;
for (int i =0; i < m; i ++) {
scanf("%d",&num[i]);
ans ^= num[i];
}
cnt = 0;
if(ans){
for (int i =0; i < m; i ++) {
if (num[i] >= (ans ^ num[i])) {//若可以通过将第i堆,取走一些,使剩下的堆异或为0,则cnt++
cnt ++;
}
}
}
printf("%d\n",cnt);
}
return0;
}
三、SG函数
将若干堆石子,看作一个状态,起始状态处有一颗棋子,则玩家的每次操作,就是将棋子沿有向无回图的边移动,移动至无法移动时,则游戏结束。
然后为每个状态x(即有向无回图的每个顶点),定义一个SG函数值,SG(x) = mex{ x所有后继y的SG(y) },mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。
必败点P SG(P) = 0;
必胜点N SG(N) > 0;
若SG(x) = k > 0,则说明,状态x的后继的sg值为0,1,...k - 1,移动x,sg值可能变为0,1,..,k - 1(与Nim博弈类似,一堆k颗石子,取任意颗后,可能是0,1,..k - 1颗)
所以,有Nim博弈可以得到,如果有m颗棋子,位于m个有向无回图中,每次可以选任意一颗棋子移动,棋子sg值分别为SG(x1),SG(x2)...SG(xm),若异或值为0,则先手必败。
如何计算SG函数
1.若可以取任意颗(Nim博弈),则SG(x) = x
(所以Nim博弈可以直接将石子数异或,不用求SG)
2.每次可以取1-m颗,则SG(x) = x % (1 + m)
3.给定k种操作,每次可以s1,s2...sk颗
//k 操作数,s[]记录每种操作可以取多少颗
void cal_sg()//预处理所有sg(x) O(maxk * maxn)
{
memset(g, 0, sizeof(g));//SG函数
for (int i = 1; i < maxn; i ++) {
memset(mp, 0, sizeof(mp));//记录所有后继的sg值,求mex
for (int j = 0; j < k; j ++) {
if(i < s[j]) break;
mp[g[i - s[j]]] = 1;
}
for (int j = 0; j < maxn; j ++) {
if(mp[j] == 0) {g[i] = j;break;}
}
}
}
int get_sg(int x)//计算单独sg(x)递归求
{
memset(g, -1, sizeof(g));
mp[0] = 0;
int i;
for (i = 0; i < k; i ++) {
if(x < s[i]) break;
if(g[x - s[i]] == -1) g[x - s[i]] = get_sg(x - s[i]);
mp[g[x - s[i]]] = 1;
}
for(i = 0;;i ++) {if(mp[i] == 0) return g[x] = i;}
}
hdu1848 Fibonacci again and again
2人3堆石子m,n,p,每次可取fibonacci数列中的个数(1,2,3,5,8....)
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn =1000 +5;
vector<int> fibo;
int g[maxn];
int mp[maxn];
void get_fibo()
{
fibo.push_back(1),fibo.push_back(2);
for (int i =2;; i ++) {
fibo.push_back(fibo[i - 1] + fibo[i -2]);
if(fibo[i] > maxn)break;
}
}
void cal_sg()//预处理计算maxn内的sg值 O(maxn * k)
{
memset(g, 0,sizeof(g));
for (int i =1; i < maxn; i ++) {
memset(mp, 0,sizeof(mp));
for (int j =0; j < fibo.size(); j ++) {
if(i < fibo[j])break;
mp[g[i - fibo[j]]] = 1;
}
for (int j =0;; j ++) {
if(mp[j] ==0) {g[i] = j;break;}
}
}
}
int main()
{
int m,n,p;
get_fibo();
cal_sg();
while (scanf("%d%d%d",&m,&n,&p) != EOF) {
if(m ==0 && n ==0 && p ==0)break;
if (g[m] ^ g[n] ^ g[p]) printf("Fibo\n");
else printf("Nacci\n");
}
return0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespacestd;
const int maxk =100 +5;
const int maxn =1e4 +5;
int s[maxk];
int g[maxn];
int k;
int get_sg(int x)//计算单独sg(x)递归求
{
if(g[x] != -1)returng[x];
int mp[maxk] = {0};//!!!貌似在全局定义,每次memset会超时?
int i;
for (i =0; i <k; i ++) {
if(x <s[i])break;
if(g[x -s[i]] == -1)g[x - s[i]] =get_sg(x -s[i]);
mp[g[x -s[i]]] =1;
}
for(i =0;;i ++) {if(mp[i] ==0)returng[x] = i;}
}
int main()
{
int m,l,t,ans;
while (scanf("%d",&k) != EOF && k) {
memset(s,0,sizeof(s));
memset(g, -1,sizeof(g));
g[0] =0;
for (int i =0; i <k; i ++) {
scanf("%d",&s[i]);
}
sort(s,s +k);
scanf("%d",&m);
for (int i =0; i < m; i ++) {
scanf("%d",&l);
ans = 0;
for (int j =0; j < l; j ++) {
scanf("%d",&t);
ans ^= get_sg(t);
}
if(ans)printf("W");
elseprintf("L");
}
printf("\n");
}
return0;
}
博弈论--从 必胜点与必败点 到 SG 函数相关推荐
- 【博弈论】博弈论入门笔记(四类基础博弈结论+SG函数)
1.巴什博奕(Bash Game): 只有一堆n个物品,两个人轮流轮流从中取物,每次最少取一个,最多取m个,最后取光的人获胜.(谁拿了最后一个谁赢) 结论: 1.if(n%(m+1) != 0) ,则 ...
- 【小组专题二:博弈论入门综述(1)】NP状态 | SG函数 | 巴什博奕、威佐夫博弈、斐波那契博弈、Nim游戏、SJ定理
博弈论综述[1] 前言 博弈与博弈论 博弈树 NP状态 SG函数(Sprague-Grundy) Sprague-Grundy Theorem 巴什博奕 Bash Game 威佐夫博弈 扩展威佐夫博弈 ...
- 牛客多校-Z-Game on grid-(博弈论+必胜态必败态转移)
M 题意: 就是给你一个n×m的棋盘,每个点要么是A,要么是B,要么是 . .然后机器人刚开始在(1,1)点,Alice先控制机器人走一步,Bob控制机器人走一步.每次只能往右或者下走,不能超过棋盘. ...
- HDU1847Good Luck in CET-4 Everybody!递推求解必胜、必败点 附三种经典博弈
Problem Description 大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此.当然,作为在考场浸润了十几载 ...
- 博弈论总结 必胜点 SG函数 巴什博弈 尼姆博弈
摘要 主要算法思想,应用领域,近年来的出题情况 博弈是信息学和数学试题中常会出现的一种类型,算法灵活多变是其最大特点,而其中有一类试题更是完全无法用常见的博弈树来进行解答. 寻找必败态即为针对此类试题 ...
- 博弈论(Nim游戏、有向图游戏之SG函数)
这里写目录标题 经典NIM游戏 Nim游戏属于公平组合游戏ICG 有向图游戏(SG函数) Mex运算 SG函数 单个有向图(一堆石子) 求SG值(记忆化递归) 有向图游戏的和 ,(多个有向图(多堆石子 ...
- [博弈论] Nim游戏及SG函数(经典+台阶+集合+拆分)
文章目录 0. 前言 1. Nim 游戏+模板题 2. 台阶 - Nim 游戏+变种题 3. Mex运算与SG函数 4. 集合 - Nim 游戏+变种题 5. 拆分 - Nim 游戏+变种题 0. 前 ...
- 博弈论 Nim游戏与SG函数
普通Nim游戏: 有若干堆石子,两人轮流从中取石子,取走最后一个石子的人为胜利者 我们判断先手必胜还是先手必败就要判断先手面对的局面是必胜态还是必败态 并且普通Nim游戏满足以下性质: 1.无法移动的 ...
- 博弈论-SG函数和SG定理
1.SG函数和SG定理是一个十分神奇的东西,有了它,绝大部分的博弈都可以被统一到这个上面,都可以使用SG函数解决.是一种解决博弈问题的十分方便的手段. 2.首先给出一些基本的定义:mex运算,这个是作 ...
最新文章
- mysql+mycat搭建稳定高可用集群,负载均衡,主备复制,读写分离
- Ubuntu15.10安装XAMPP
- Google Gears 指南
- 使用PHP生成分享图片
- C++使用JSON的序列化与反序列化
- leetcode270. 最接近的二叉搜索树值
- 自定义oracle异常,Oracle自定义异常
- P1574: [Usaco2009 Jan]地震损坏Damage
- 未成年人互联网普及率达99% 腾讯等倡议加强青少年网络素养教育
- 不是 group by 表达式_12正则表达式
- ExtJs启用与禁用工具栏
- 敏捷开发流程简单了解
- 数据结构与算法---常用三大排序算法
- 谷歌5款插件推荐:谷歌工具类、翻译类扩展程序
- 计算机青年教师基本功大赛,北京高校第九届青年教师基本功比赛纪实
- 米勒-拉宾质数判断证明
- 用python定义一个员工类_Python与类一起工作
- python信号端点检测_语音信号端点检测
- JavaWeb项目中出现faceted project problem问题是什么原因?
- python+playwright 学习-17.模拟手机浏览器测试
热门文章
- 二分法求多项式单根 — c语言
- 《侠盗猎车手-罪恶都市》秘籍
- 20060525: Office 2007
- 【CSS3 基础】全面入门学习
- vs code 离线安装 CodeLLDB 包
- 考计算机专业要理科好吗,大学想念计算机系是不是该高中读理科?
- 计算机电池的性能参数,【戴尔灵越3567笔记本电脑使用总结】体积|电池|性能_摘要频道_什么值得买...
- Oracle使用脚本导出数据(spool)
- sin函数对照表_三角函数值对照表
- 【数组】深析 “数组名称”