博弈论

博弈论入门

  • 巴什博弈
  • 尼姆博弈(Nim)
    • 台阶-Nim游戏
    • 集合-Nim游戏
    • 拆分-Nim游戏
  • 威佐夫博弈
  • 斐波那契博弈

1318. 取石子游戏(巴什博弈)

题目描述

有一种有趣的游戏,玩法如下:

玩家: 2 人;

道具: N 颗石子;

规则:

  1. 游戏双方轮流取石子;
  2. 每人每次取走若干颗石子(最少取 1 颗,最多取 K 颗);
  3. 石子取光,则游戏结束;
  4. 最后取石子的一方为胜。
    假如参与游戏的玩家都非常聪明,问最后谁会获胜?

输入格式

输入仅一行,两个整数 NK

输出格式

输出仅一行,一个整数,若先手获胜输出 1,后手获胜输出 2

数据范围

1N105,
1KN

输入样例:

23 3

输出样例:

1

解题思路

结论:n 整除 k+1 时,先手必败

分类讨论:

  1. nk,此时先手可以直接拿走所有石子,先手必胜
  2. n=m×(k+1),对于每 k+1 堆石子(共 m 堆石子),不管先手怎么拿,后手都可以拿完这堆石子,先手必败
  3. n=m×(k+1)+x,0<x<k+1,先手可以将拿走 x 个石子,此时为必败态,转移到了后手,先手必胜
    故:n 整除 k+1 时,先手必败

代码

#include<bits/stdc++.h> using namespace std; int n,k; int main() { scanf("%d%d",&n,&k); if(n%(k+1))puts("1"); else puts("2"); return 0; }

P2197 【模板】nim 游戏(尼姆博弈(Nim))


题目描述

甲,乙两个人玩 nim 取石子游戏。

nim 游戏的规则是这样的:地上有 n 堆石子(每堆石子数量小于 104),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n 堆石子的数量,他想知道是否存在先手必胜的策略。

输入格式

本题有多组测试数据。

第一行一个整数 TT10,表示有 T 组数据

接下来每两行是一组数据,第一行一个整数 n,表示有 n 堆石子,n104

第二行有 n 个数,表示每一堆石子的数量.

输出格式

T 行,每行表示如果对于这组数据存在先手必胜策略则输出 Yes,否则输出 No

输入

2 2 1 1 2 1 0

输出

No Yes

解题思路

结论:a1a2an=0 时,先手必败

证明:

  1. 000=0
  2. 设有 a1a2an=xx>0,设 x 最高位为第 k 位,则显然,在 1n 堆石子中,必然存在一个 ai,其第 k 位为 1,且有 xai<ai,则从该堆石子中取 aixai 堆石子,此时该堆石子有 xai 个,故:a1a2aixan=0
  3. 设有 a1a2an=0,设从 i 堆中取走石子,该堆剩下 ak 个石子,设 a1a2akan=0,两个式子左右异或,有 aiak=0,即 ai=ak,显然不成立,故有:a1a2an=0 时,取石子后其异或值不为 0

最终状态一定是情况1,故有:a1a2an=x,x>0 时,先手必胜

代码

  • 时间复杂度:O(n)
#include<bits/stdc++.h> using namespace std; int main() { int t,n,res,other; for(scanf("%d",&t);t;t--) { scanf("%d%d",&n,&res); while(--n) { scanf("%d",&other); res^=other; } puts(res?"Yes":"No"); } return 0; }

892. 台阶-Nim游戏

题目描述

现在,有一个 n 级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i1)。

两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。

已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式

第一行包含整数 n

第二行包含 n 个整数,其中第 i 个整数表示第 i 级台阶上的石子数 ai

输出格式

如果先手方必胜,则输出 Yes

否则,输出 No

数据范围

1n105,
1ai109

输入样例:

3 2 1 3

输出样例:

Yes

解题思路

a1a3a5=0 时,先手必败

a1a3a5=0 时,如果此时先手从偶数阶拿石子,此时后手可以把先手从偶数阶拿到奇数阶的石子拿到下一个偶数阶上,此时先手面临的状态依然是 a1a3a5=0;如果此时先手从奇数阶拿石子,根据nim游戏证明,此时 a1a3a5=x,x0,而后手可以从奇数阶中拿石子,使得先手依然面临 a1a3a5=0 的状态,最终的结束状态一定有 a1a3a5=0,故有:a1a3a5=0 时,先手必败

  • 时间复杂度:O(n)

代码

#include<bits/stdc++.h> using namespace std; int n,x,res; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); if(i&1) res^=x; } puts(res?"Yes":"No"); return 0; }

893. 集合-Nim游戏

题目描述

给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S

现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式

第一行包含整数 k,表示数字集合 S 中数字的个数。

第二行包含 k 个整数,其中第 i 个整数表示数字集合 S 中的第 i 个数 si

第三行包含整数 n

第四行包含 n 个整数,其中第 i 个整数表示第 i 堆石子的数量 hi

输出格式

如果先手方必胜,则输出 Yes

否则,输出 No

数据范围

1n,k100,
1si,hi10000

输入样例:

2 2 5 3 2 4 7

输出样例:

Yes

解题思路

定义:sg(x)=mex(sg(y1,sg(y2),sg(y3),),其中 y1,y2,y3, 为 状态 x 的后继状态,mex 运算为集合中未出现的最小自然数
结论:sg(x1)sg(x2)sg(x3)=0 时,其中 x1,x2,x3, 为初态,先手必败

对于一堆石子来说,假设初态为 x,终态为 e,由定义:sg(e)=0,即 sg(e)=0 时说明没有后继状态,为必败态。假设先手初态时:sg(x)=k,k0,由mex定义:其后继状态 y,一定有 sg(y)=0,1,2,,k1,故先手一定可以使后手面临 sg(y)=0 的状态,即必败态
对于好多堆石子来说,存在初态 x1,x2,x3,,最终状态一定是 0,0,0,,即 sg(0)sg(0)sg(0)=0。假设 sg(x1)sg(x2)sg(x3)=k,k0,同nim游戏的分析方法,即:假设 k 的最高位为 t,则 sg(x1),sg(x2),sg(x3) 中存在第 t 位为 1,假设为 sg(xi),由于 ksg(xi)<sg(xi),由mex定义,状态 xi 一定可以转移到状态 sg(y)=ksg(xi) 上,此时后手面对 sg(x1)sg(x2)sg(x3)ksg(xi)=0 的状态,任意地,设其由 sg(xj) 转移到 sg(y),由 mex定义:sg(y)<sg(xj),假设 sg(x1)sg(x2)sg(x3)sg(y)=0,与上式左右异或,有 sg(xj)sg(y)=0,即 sg(xj)=sg(y),显然不成立,则有 sg(x1)sg(x2)sg(x3)sg(y)0,最后状态一定有:sg(0)sg(0)sg(0)=0,故有:sg(x1)sg(x2)sg(x3)=0 时,其中 x1,x2,x3, 为初态,先手必败

另外,本题,采用了记忆化搜索,大大降低了时间复杂度

  • 时间复杂度:O(nk)

代码

#include<bits/stdc++.h> using namespace std; int n,k,x,s[105],f[10005]; int sg(int x) { if(f[x]!=-1)return f[x]; unordered_set<int> S; for(int i=1;i<=k;i++) if(s[i]<=x)S.insert(sg(x-s[i])); for(int i=0;;i++) if(!S.count(i)) return f[x]=i; } int main() { scanf("%d",&k); for(int i=1;i<=k;i++)scanf("%d",&s[i]); memset(f,-1,sizeof f); int res=0; scanf("%d",&n); while(n--) { scanf("%d",&x); res^=sg(x); } puts(res?"Yes":"No"); return 0; }

894. 拆分-Nim游戏

题目描述

给定 n 堆石子,两位玩家轮流操作,每次操作可以取走其中的一堆石子,然后放入两堆规模更小的石子(新堆规模可以为 0,且两个新堆的石子总数可以大于取走的那堆石子数),最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式

第一行包含整数 n

第二行包含 n 个整数,其中第 i 个整数表示第 i 堆石子的数量 ai

输出格式

如果先手方必胜,则输出 Yes

否则,输出 No

数据范围

1n,ai100

输入样例:

2 2 3

输出样例:

Yes

解题思路

题意为每次操作将一堆变为两堆规模更小的石子,采用sg函数解决本题:先求出每一堆石子的sg函数,即对于一堆含有 x 个石子的石子堆可变为石子数为 y1,y2 的两堆石子,其中 y1,y2<x,两堆石子对应的sg函数sg(y1)sg(y2),这是解决本题的关键,然后将所有石子堆的sg函数异或即可解决本题~

  • 时间复杂度:O(n3)

代码

#include<bits/stdc++.h> using namespace std; int n,x,f[105]; int sg(int x) { if(f[x]!=-1)return f[x]; unordered_set<int> S; for(int i=0;i<x;i++) for(int j=0;j<=i;j++) S.insert(sg(i)^sg(j)); for(int i=0;;i++) if(!S.count(i)) return f[x]=i; } int main() { scanf("%d",&n); memset(f,-1,sizeof f); int res=0; while(n--) { scanf("%d",&x); res^=sg(x); } puts(res?"Yes":"No"); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/15487468.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(215)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示