移棋子游戏
给定一个有 N 个节点的有向无环图,图中某些节点上有棋子,两名玩家交替移动棋子。
玩家每一步可将任意一颗棋子沿一条有向边移动到另一个点,无法移动者输掉游戏。
对于给定的图和棋子初始位置,双方都会采取最优的行动,询问先手必胜还是先手必败。
输入格式
第一行,三个整数N,M,K,N 表示图中节点总数,M 表示图中边的条数,K 表示棋子的个数。
接下来 M 行,每行两个整数 X,Y 表示有一条边从点 X 出发指向点 Y。
接下来一行, K 个空格间隔的整数,表示初始时,棋子所在的节点编号。
节点编号从 1 到 N。
输出格式
若先手胜,输出 win,否则输出 lose。
数据范围
1≤N≤2000,
1≤M≤6000,
1≤K≤N
输入样例:
6 8 4
2 1
2 4
1 4
1 5
4 5
1 3
3 5
3 6
1 2 4 6
输出样例:
win
# 题解
# 代码
```
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
using namespace std;
const int N = 2005;
const int M = 6005;
int n, m, k;
int h[N], e[M], ne[M], idx; // 邻接表存图
int sg[N]; // 存所有被计算过的点的 SG 函数值
int res;
inline void add(int u, int v) // 加边函数。从点 u 向点 v 连一条有向边
{
e[ ++ idx] = v;
ne[idx] = h[u];
h[u] = idx;
}
int SG(int u)
{
if (~sg[u]) return sg[u]; // 如果当前 sg[u] 不是 -1,那么说明该点的 SG 函数值已经被计算过了,直接返回
set<int> S; // 否则要建一个集合 S,存该点能到的所有点的 SG 函数值
for (int i = h[u]; i; i = ne[i]) // 遍历点 u 能到达的所有点
S.insert(SG(e[i])); // 计算该点的 SG 函数值,并放入集合 S
for (int i = 0; ; i ++ ) // 从 0 开始枚举所有非负整数
if (!S.count(i)) // 如果该值没有在 S 中出现过
{
sg[u] = i; // 那么将该值记录在 sg[u] 中并返回
return i;
}
}
int main()
{
scanf("%d %d %d", &n, &m, &k); // 读入题目中 N, M, K
for (int i = 0; i < m; i ++ ) // 读入 M 条边并建图
{
int u, v;
scanf("%d %d", &u, &v);
add(u, v);
}
memset(sg, -1, sizeof sg); // 先将 sg 数组中的所有值初始化成 -1,表示没有记录过
while (k -- ) // 读入 K 个棋子所在的点
{
int u;
scanf("%d", &u);
res ^= SG(u);
}
if (res) puts("win"); // 如果 res 不为 0,那么输出 win
else puts("lose"); // 否则输出 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框架的用法!