G60 Nim游戏 SG函数
视频链接:https://www.bilibili.com/video/BV1eT411B7A8/
给定一个有 𝑛 个节点和 𝑚 条边的有向无环图,𝑘 个棋子所在的节点编号。
两名玩家交替移动棋子,每次只能将任意一颗棋子沿有向边移到另一个点,无法移动者视为失败。
如果两人都采用最优策略,问先手是否必胜。
思路:每个棋子都是孤立的,𝑘 个棋子拆分成 𝑘 个有向图游戏,
利用 SG 定理判断即可
时间:O(k*n)
#include <cstdio> #include <cstring> #include <set> using namespace std; const int N=2005,M=10005; int n,m,k,a,b,x; int h[N],to[M],ne[M],tot; //邻接表 int f[N]; void add(int a,int b){ to[++tot]=b,ne[tot]=h[a],h[a]=tot; } int sg(int u){ // 记忆化搜索 if(f[u]!=-1) return f[u]; // 把子节点的sg值插入集合 set<int> S; for(int i=h[u];i;i=ne[i]) S.insert(sg(to[i])); // mex运算求当前节点的sg值并记忆 for(int i=0; ;i++) if(!S.count(i)) return f[u]=i; } int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=0;i<m;i++) scanf("%d%d",&a,&b), add(a,b); memset(f,-1,sizeof f); int res=0; for(int i=0;i<k;i++) scanf("%d",&x),res^=sg(x); if(res) puts("win"); else puts("lose"); }
#include <cstdio> #include <cstring> #include <map> using namespace std; const int N=2005,M=10005; int n,m,k,a,b; int h[N],to[M],ne[M],tot; //邻接表 int f[N]; void add(int a,int b){ to[++tot]=b,ne[tot]=h[a],h[a]=tot; } int sg(int u){ // 记忆化搜索 if(f[u]!=-1) return f[u]; // 把子节点的sg值插入集合 map<int,int> mp; for(int i=h[u];i;i=ne[i]) mp[sg(to[i])]=1; // mex运算求当前节点的sg值并记忆 for(int i=0; ;i++) if(!mp.count(i))return f[u]=i; } int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=0;i<m;i++) scanf("%d%d",&a,&b), add(a,b); memset(f,-1,sizeof f); int res=0; for(int i=0;i<k;i++) scanf("%d",&a),res^=sg(a); if(res) puts("win"); else puts("lose"); }
2. 集合型 Nim游戏
给定 𝑚 个整数组成的集合 𝑎𝑖,给定 𝑛 堆石子的数量 𝑏𝑖。
两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须是集合 𝑎 中的整数,最后无法进行操作的人视为失败。
如果两人都采用最优策略,问先手是否必胜。
思路:每堆石子都是孤立的,把 𝑛 堆石子看做 𝑛 个有向图游戏
#include <cstring> #include <iostream> #include <algorithm> #include <set> using namespace std; const int N=1005, M=10005; int n,m,x; int a[N], f[M]; int sg(int x){ // 记忆化搜索 if(f[x]!=-1) return f[x]; // 把子节点的sg值插入集合 set<int> S; for(int i=0;i<m;i++) if(x>=a[i]) S.insert(sg(x-a[i])); // mex运算求当前节点的sg值并记忆 for(int i=0; ;i++) if(!S.count(i)) return f[x]=i; } int main(){ cin>>m; for(int i=0; i<m; i++)cin>>a[i]; cin>>n; memset(f,-1,sizeof f); int res=0; for(int i=0; i<n; i++) cin>>x, res^=sg(x); if(res) puts("Yes"); else puts("No"); }
给定一张 𝑛×𝑚 的矩形网格纸,两名玩家轮流行动。
在每一次行动中,可以任选一张矩形网格纸,沿着某一行或某一列的格线,把它剪成两部分。
首先剪出 1×1 的格纸的玩家获胜。
如果两人都采用最优策略,问先手是否必胜。1≤𝑛,𝑚≤200
#include<iostream> #include<algorithm> #include<cstring> #include<set> using namespace std; const int N=210; int n,m; int f[N][N]; int sg(int a,int b){ // 记忆化搜索 if(f[a][b]!=-1) return f[a][b]; // 把子节点的sg值插入集合 set<int> S; for(int i=2;i<=a-2;i++) S.insert(sg(i,b)^sg(a-i,b)); for(int i=2;i<=b-2;i++) S.insert(sg(a,i)^sg(a,b-i)); // mex运算求当前节点的sg值并记忆 for(int i=0; ;i++) if(!S.count(i)) return f[a][b]=f[b][a]=i; } int main(){ memset(f,-1,sizeof f); while(cin>>n>>m) puts(sg(n,m)?"WIN":"LOSE"); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!