当前位置: 首页 > news >正文

公开游戏、基于有向图的游戏

目录

〇,背景

一,公开游戏、策梅洛定理

1,公开游戏

2,策梅洛定理

二,有向图游戏

1,狭义有向图游戏

2,广义有向图游戏

3,狭义有向图游戏的SG数

4,Bash Game

力扣 292. Nim 游戏(正向Bash)

51Nod 1066 Bash游戏(正向Bash)

51Nod 1067 Bash游戏 V2(正向拓展Bash)

51Nod 1068 Bash游戏 V3(正向拓展Bash)

三,公平游戏、公平组合游戏、Sprague-Grundy定理

1,公平游戏

2,公平组合游戏

3,Sprague-Grundy定理

4,Nim Game

HDU 2176 取(m堆)石子游戏 (正向Nim)

POJ 1704 Georgia and Bob (正向Nim的变形)

FZU 1928 硬币翻转游戏(正向1.5维Nim)

HDU 1907 John (反向Nim)

力扣 1908. Nim 游戏 II

5,Green Hackenbush

力扣 2005. 斐波那契树的移除子树游戏

四,有向有环图游戏

力扣 913. 猫和老鼠


〇,背景

本文收录基于有向图的博弈,提供通用的解法。

本文涉及5种博弈类型:公开游戏、有向图游戏、有向有环图游戏、公平游戏、公平组合游戏

五者的关系:公开游戏包括有向图游戏,有向图游戏包括公平游戏,公平游戏包括公平组合游戏,而有向有环图游戏是单独的

其中有向图游戏指的是基于有向无环图的游戏,有向有环图游戏是基于可能有环的有向图的游戏。

PS:在有向无环图上,我们定义只有胜负的游戏,而在可能有环的有向图上,我们定义有胜负平三种结局的游戏。

一,公开游戏、策梅洛定理

1,公开游戏

若一个游戏满足如下条件:

  1. 双人、回合制;
  2. 信息完全公开(perfect information);
  3. 无随机因素(deterministic);
  4. 必然在有限步内结束;
  5. 没有平局;

则称之为公开游戏。

2,策梅洛定理

策梅洛定理:公开游戏中的任何一个状态,要么先手有必胜策略,要么后手有必胜策略(下文把这两种状态分别称为“胜态”、“败态”)。

常见的牌类游戏大多不满足条件2、3。

常见的棋类游戏(如井字棋、五子棋、围棋、象棋、跳棋)大多满足条件2、3,在正式竞技中也会通过禁止循环的方式保证条件4,但不一定满足条件5。

二,有向图游戏

1,狭义有向图游戏

在一张有向无环图中,某一个节点上有一颗棋子,两名玩家轮流将棋子沿有向边移动,最先不能移动的一方输。

这里的绿色节点是败态,粉红色是胜态,整个图可以分为4层:

自底向上推导:

底层是最右边的最0层,是败态。

有一条边指向任意败态节点的就是胜态节点,所以第1层的2个节点是胜态。

所有边都指向胜态节点的节点的就是败态节点,所以第2层的1个节点是败态。

第3层的1个节点是胜态。

这个分层推导的例子,可以推广到所有的有向无环图,最终每个节点都有一个非负层数,层数的奇偶性直接决定是胜态还是败态。

2,广义有向图游戏

(1)推广1

在狭义有向图游戏的基础上进行推广,对于任意有向有环图,定义其中一些节点为胜或者负,两名玩家轮流将棋子沿有向边移动。

要想保证最终一定会走到定义了胜负的节点,需要保证出度为0的点一定定义了胜负,出度不为0的每一个点都可以定义或不定义胜负。

这一类游戏全部可以转化成狭义有向图游戏,具体方法是2步:

第1步,把所有定义了胜负的节点,出度置为0,即从他们出发的有向边全部删除。

第2步,新增1个定义为负的节点X,把所有定义成胜的节点,出度置为1,即从他们连一条有向边到X。

这样就转化成了狭义有向图游戏。

(2)推广2

因为有向图本身就是个建模工具,能表达丰富的信息,所以很多博弈都可以转化成有向图游戏(转化成狭义有向图游戏,或有向图游戏推广1),我们把这些游戏都称为广义有向图游戏。

一般说有向图游戏,指的都是广义有向图游戏,属于公开游戏的一种。

公开游戏可能不是广义有向图游戏,因为公开游戏可以不满足每步的走法数有限

3,狭义有向图游戏的SG数

SG数的定义:

SG(x)=mex\{SG(y)| x\rightarrow y\}

其中x->y表示存在x指向y的一条边,mex(S)表示不在集合S中的最小自然数。

SG为0则是败态,SG>0则是胜态。

无论是分层的方式,还是SG数,都可以通过遍历来求出所有状态是胜态还是负态。

4,Bash Game

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗。

正向:谁拿完就判胜,规律是N是K+1的倍数时先手负,否则先手胜

反向:谁拿完就判负,规律是N-1是K+1的倍数时先手负,否则先手胜

力扣 292. Nim 游戏(正向Bash)

题目:

你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。

你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。

示例:

输入: 4
输出: false 
解释: 如果堆中有 4 块石头,那么你永远不会赢得比赛;
     因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。

这分明是Bash Game,不是Nim Game

代码:

class Solution {
public:bool canWinNim(int n) {return n%4;}
};

51Nod 1066 Bash游戏(正向Bash)

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。

例如N = 3,K = 2。无论A如何拿,B都可以拿到最后1颗石子。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数N,K。中间用空格分隔。(1 <= N,K <= 10^9)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

4
3 2
4 2
7 3
8 3

Sample Output

B
A
A
B
#include<iostream>
using namespace std;int main()
{int a,b;cin >> a;while (cin >> a >> b) {if (a % ++b)cout << "A\n";else cout << "B\n";}return 0;
}

51Nod 1067 Bash游戏 V2(正向拓展Bash)

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次只能拿1,3,4颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。

例如N = 2。A只能拿1颗,所以B可以拿到最后1颗石子。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

3
2
3
4

Sample Output

B
A
A

思路:以7为周期

#include<iostream>
using namespace std;int main()
{int a;cin >> a;while (cin >> a) {if (a % 7 == 0 || a % 7 == 2)cout << "B\n";else cout << "A\n";}return 0;
}

51Nod 1068 Bash游戏 V3(正向拓展Bash)

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次拿的数量只能是2的正整数次幂,比如(1,2,4,8,16....),拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。

例如N = 3。A只能拿1颗或2颗,所以B可以拿到最后1颗石子。(输入的N可能为大数)

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000) 第2 - T + 1行:每行1个数N。(1 <= N <= 10^1000)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

3
2
3
4

Sample Output

A
B
A

思路:2的幂其实就是mod 3等于1或2,所以只需要看总数是不是3的倍数

#include<iostream>
using namespace std;int main()
{int a;cin >> a;string s;while (a--) {cin >> s;int k = 0;for (int i = 0; i < s.length(); i++)k += s[i];if (k % 3 == 0)cout << "B\n";else cout << "A\n";}return 0;
}

三,公平游戏、公平组合游戏、Sprague-Grundy定理

1,公平游戏

若一个游戏满足以下条件:
  1. 双人、回合制;
  2. 信息完全公开(perfect information);
  3. 无随机因素(deterministic);
  4. 必然在有限步内结束,且每步的走法数有限(finite);
  5. 没有平局;
  6. 双方可采取的行动及胜利目标都相同(impartial);
  7. 这个胜利目标是自己亲手达成终局状态,或者说走最后一步者为胜(normal play);

则称之为公平游戏。

公平游戏都属于有向图游戏

虽然有向图游戏中的胜负很明确,但是如果图非常大,则遍历需要的时间很长。

2,公平组合游戏

如果一个公平游戏的每个状态,都可以表示成若干个子状态的组合,且游戏每一次操作,都只能对一个子状态进行操作,则称之为公平组合游戏。

公平组合游戏都属于公平游戏

公平游戏可以表示成有向图,而公平组合游戏可以表示成若干个有向连通分量。

3,Sprague-Grundy定理

公平组合游戏中,一个状态的SG数,是所有子状态的SG数的异或和。

PS:只要学过NIM博弈,就很好理解了,每个公平组合游戏都可以映射成一个NIM博弈,其中每个有向连通分量,就是NIM博弈中的一个石头堆,整个有向图就是NIM博弈中的所有石头堆。

4,Nim Game

Nim is a mathematical game of strategy in which two players take turns removing (or "nimming") objects from distinct heaps or piles. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap or pile. Depending on the version being played, the goal of the game is either to avoid taking the last object or to take the last object.

正向Nim游戏就是谁拿完最后一堆获胜,规律很简单,只要保持自己每次拿完之后,所有堆的数量异或和为0 即可。

即:异或和为0时先手负,不为0时先手胜

反向是谁拿完就判负,规律比较复杂:如果至少有一堆数量大于1,那么,异或和为0时先手负,不为0时先手胜,否则,异或和为0时先手胜,不为0时先手负。

Nim游戏的SG数:一堆石头的SG数就是石头数。

HDU 2176 取(m堆)石子游戏 (正向Nim)

题目:
Description

m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个.
Input

输入有多组.每组第1行是m,m<=200000. 后面m个非零正整数.m=0退出. 
Output

先取者负输出No.先取者胜输出Yes,然后输出先取者第1次取子的所有方法.如果从有a个石子的堆中取若干个后剩下b个后会胜就输出a b.参看Sample Output. 
Sample Input

2
45 45
3
3 6 9
5
5 7 8 9 10
0
Sample Output

No
Yes
9 5
Yes
8 1
9 0
10 3

Nim游戏早就被研究透了,我就不扯了,直接说结论。

取r为所有数的异或总值,如果r为0那么先手败,否则先手胜。

如果先手胜的话,他的策略是调整1个数,使得所有数的异或总值为0

也就是说,取某个x,将x变成x^r

只要x大于x^r,这个操作就是允许的。

这样的x,至少存在一个,可能会有很多个,本题要我们全部输出。

代码:
 

#include<iostream>
using namespace std;int num[200005];int main()
{	int m, r;while (scanf("%d",&m)){if (m == 0)break;r = 0;for (int i = 0; i < m; i++){scanf("%d", &num[i]);r = r^num[i];}if (r == 0)printf("No\n");else{printf("Yes\n");for (int i = 0; i < m; i++)if (num[i] >(num[i] ^ r))printf("%d %d\n", num[i], num[i] ^ r);}}return 0;
}

POJ 1704 Georgia and Bob (正向Nim的变形)

题目:


Georgia and Bob decide to play a self-invented game. They draw a row of grids on paper, number the grids from left to right by 1, 2, 3, ..., and place N chessmen on different grids, as shown in the following figure for example: 


Georgia and Bob move the chessmen in turn. Every time a player will choose a chessman, and move it to the left without going over any other chessmen or across the left edge. The player can freely choose number of steps the chessman moves, with the constraint that the chessman must be moved at least ONE step and one grid can at most contains ONE single chessman. The player who cannot make a move loses the game.

Georgia always plays first since "Lady first". Suppose that Georgia and Bob both do their best in the game, i.e., if one of them knows a way to win the game, he or she will be able to carry it out. 

Given the initial positions of the n chessmen, can you predict who will finally win the game? 
Input
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case contains two lines. The first line consists of one integer N (1 <= N <= 1000), indicating the number of chessmen. The second line contains N different integers P1, P2 ... Pn (1 <= Pi <= 10000), which are the initial positions of the n chessmen.
Output
For each test case, prints a single line, "Georgia will win", if Georgia will win the game; "Bob will win", if Bob will win the game; otherwise 'Not sure'.
Sample Input
2
3
1 2 3
8
1 5 6 7 9 12 14 17
Sample Output
Bob will win
Georgia will win

题意:

有N个棋子排成一排,位置分别是所给定的整数,双方轮流把其中一个棋子往左移,但是不能超过左边的棋子,也不能移到同一个位置,只能移到它的右边一格。

问先手还是后手有必胜策略。

思路:

两两一组,如果是奇数个,最左边的单独一组,通过差分,化成Nim博弈

代码:

#include<iostream>
#include<algorithm>
using namespace std;int main()
{int T, n, s, l[1005];cin >> T;while (T--){cin >> n;s = 0;for (int i = 0; i < n; i++)cin >> l[i];sort(l, l + n);if (n % 2)s = l[0] - 1;for (int i = n % 2; i < n; i += 2)s ^= l[i + 1] - l[i] - 1;if (s)cout << "Georgia will win\n";else cout << "Bob will win\n";}return 0;
}

FZU 1928 硬币翻转游戏(正向1.5维Nim)

题目:

Description

Alice和Bob决定玩一种硬币翻转游戏。游戏的最初有n * m个硬币被放在一个棋盘上,有的正面朝上,有的反面朝上,当游戏开始的时候,他们轮流对硬币进行操作。
游戏的规则是这样的,当轮到一个人操作的时候,他必须挑选两枚硬币,将它们分别翻转(不是交换),同时这两枚硬币必须满足以下条件:

1. 两枚硬币必须在同一行或同一列

2. 两枚硬币中最右下的那枚必须是从正面翻转到反面

当一方无法做出满足上面要求的操作时,这一方就输了,游戏结束。

比如,当游戏初始状态是棋盘1时,Alice翻转完2枚硬币,可以将其变成 棋盘2的状态或棋盘3的状态。

Alice总是第一个操作,假设Alice和Bob都采取最优策略来玩这个游戏,请 你写一个程序判断Alice是否能赢。

Input

首先第一行为T,表示有T组数据。接下来为每组数据的结构:
每组测试数据的第一行有两个整数n和m (2 <= n, m <= 1000),代表棋盘上排列着n行m列的硬币,接下来n行,每行 m个字符描述硬币的初始状态,’F’代表硬币正面朝上,’B’代表硬币反面朝上。

Output

对于每组数据输出一行先输出组数(从1开始),接下来如果Alice能赢,输出YES,否则输出NO.
Sample Input

2
2 2
FB
BB
2 2
BF
BB
Sample Output

Case 1: NO
Case 2: YES

我只能说这个OJ绝对有毒啊

#include<iostream>
using namespace std;int main()
{int t, n, m, ans = 0;cin >> t;char c;for (int cas = 1; cas <= t; cas++){cin >> n >> m;for (int i = 0; i < n; i++)for (int j = 0; j < m; j++){cin >> c;if (c == 'F')ans ^= (i^j);}if (ans)cout << "Case " << cas << ": " << "YES" << endl;else cout << "Case " << cas << ": " << "NO" << endl;}return 0;
}

HDU 1907 John (反向Nim)

Little John is playing very funny game with his younger brother. There is one big box filled with M&Ms of different colors. At first John has to eat several M&Ms of the same color. Then his opponent has to make a turn. And so on. Please note that each player has to eat at least one M&M during his turn. If John (or his brother) will eat the last M&M from the box he will be considered as a looser and he will have to buy a new candy box.

Both of players are using optimal game strategy. John starts first always. You will be given information about M&Ms and your task is to determine a winner of such a beautiful game.
 

Input

The first line of input will contain a single integer T – the number of test cases. Next T pairs of lines will describe tests in a following format. The first line of each test will contain an integer N – the amount of different M&M colors in a box. Next line will contain N integers Ai, separated by spaces – amount of M&Ms of i-th color.

Constraints:
1 <= T <= 474,
1 <= N <= 47,
1 <= Ai <= 4747
 

Output

Output T lines each of them containing information about game winner. Print “John” if John will win the game or “Brother” in other case.
 

Sample Input

2
3
3 5 1
1
1

Sample Output

John
Brother

思路:

flag表示至少有1堆的数量大于1,s表示异或和,那么if (flag ^ (s > 0))则后手胜,否则先手胜。

代码:

#include<iostream>
using namespace std;int main()
{int n, a[50];cin >> n;while (cin >> n) {int flag = 0, s = 0;for (int i = 0; i < n; i++) {cin >> a[i];if (a[i] > 1)flag = 1;s ^= a[i];}if (flag ^ (s > 0))cout << "Brother\n";else cout << "John\n";}return 0;
}

力扣 1908. Nim 游戏 II

Alice 和 Bob 交替进行一个游戏,由 Alice 先手。

在游戏中,共有 n 堆石头。在每个玩家的回合中,玩家需要 选择 任一非空石头堆,从中移除任意 非零 数量的石头。如果不能移除任意的石头,就输掉游戏,同时另一人获胜。

给定一个整数数组 piles ,piles[i] 为 第 i 堆石头的数量,如果 Alice 能获胜返回 true ,反之返回 false 。

Alice 和 Bob 都会采取 最优策略 。

示例 1:

输入:piles = [1]
输出:true
解释:只有一种可能的情况:
- 第一回合,Alice 移除了第 1 堆中 1 块石头。piles = [0]。
- 第二回合,Bob 没有任何石头可以移除。Alice 获胜。
示例 2:

输入:piles = [1,1]
输出:false
解释:可以证明,Bob一定能获胜。一种可能的情况:
- 第一回合,Alice 移除了第 1 堆中 1 块石头。 piles = [0,1]。
- 第二回合,Bob 移除了第 2 堆中 1 块石头。 piles = [0,0]。
- 第三回合,Alice 没有任何石头可以移除。Bob 获胜。
示例 3:

输入:piles = [1,2,3]
输出:false
解释:可以证明,Bob一定能获胜。一种可能的情况:
- 第一回合,Alice 移除了第 3 堆中 3 块石头。 piles = [1,2,0]。
- 第二回合,Bob 移除了第 2 堆中 1 块石头。 piles = [1,1,0]。
- 第三回合,Alice 移除了第 1 堆中 1 块石头。piles = [0,1,0]。
- 第四回合,Bob 移除了第 2 堆中 1 块石头。 piles = [0,0,0]。
- 第三回合,Alice 没有任何石头可以移除。Bob 获胜。
 

提示:

n == piles.length
1 <= n <= 7
1 <= piles[i] <= 7
 

进阶:你能想出一个 线性时间 的解决方案吗?虽然这一答案可能超出了面试所需的范围,但了解它可能会很有趣。

class Solution {
public:bool nimGame(vector<int>& piles) {int ans=0;for(auto x:piles)ans^=x;return ans;}
};

5,Green Hackenbush

Hackenbush游戏是通过移除一个有根图的某些边并自动不和根相连的部分,最终没有边可移除则判负。

Green Hackenbush,每条边都是绿色的,双方都可以操作。

Blue-Red Hackenbush,有些边是蓝色,有些边是红色,而玩家一只能删除蓝色边,玩家二只能删除红色边。

力扣 2005. 斐波那契树的移除子树游戏

斐波那契树是一种按这种规则函数 order(n) 创建的二叉树:

  • order(0) 是空树。
  • order(1) 是一棵只有一个节点的二叉树。
  • order(n) 是一棵根节点的左子树为 order(n - 2) 、右子树为 order(n - 1) 的二叉树。

Alice 和 Bob 在玩一种关于斐波那契树的游戏,由 Alice 先手。在每个回合中,每个玩家选择一个节点,然后移除该节点其子树。只能删除根节点 root 的玩家输掉这场游戏。

给定一个整数 n,假定两名玩家都按最优策略进行游戏,若 Alice 赢得这场游戏,返回 true 。若 Bob 赢得这场游戏,返回 false 。

一棵二叉树的子树 tree 是由 tree 中某个节点及其所有后代节点组成的树。树 tree 也可当作自身的子树。

示例 1:

输入: n = 3
输出: true
解释:
Alice 移除右子树中的节点 1。
Bob 要么移除左子树中的 1,要么移除右子树中的 2。
Alice 可以移除 Bob 未移除的任意节点。
Bob 只能删除根节点 3,所以 Bob 输了。
返回 true,因为 Alice 赢了。

示例 2:

输入: n = 1
输出: false
解释:
Alice 只能移除根节点 1, 所以 Alice 输了。
返回 false,因为 Alice 输了。

示例 3:

输入: n = 2
输出: true
解释:
Alice 删除节点 1.
Bob 只能删除根节点 2,所以 Bob 输了。
返回 true,因为 Alice 赢了。

提示:

  • 1 <= n <= 100

思路:Green Hackenbush的一个特例。

如果是树而不是图,那么sg函数的递推式就是sg(root) = (sg(left)+1) ^ (sg(right)+1)

所以本题的sg值就是0 1 3 6 3 3 0 5 7 14 7 7 0 9 11 6 11 0 13......

class Solution {
public:bool findGameWinner(int n) {return n%6-1;}
};

四,有向有环图游戏

在一张可能有环的有向图中,某一个节点上有一颗棋子,两名玩家轮流将棋子沿有向边移动,要么走到预先定义了胜负平的节点,要么沿着环移动判定为平局。

策略:

(1)能走到负节点,那就走到负节点,当前就是胜节点

(2)如果只能走到胜节点,那我就是负节点

(3)反复运用前2条规则之后,还没有确定的节点,都是平节点,包括有环的情况。

按照这个策略,其实求解过程也不难。

class SG2 {
public://输入每个节点的出度out, 反图rg,已知胜1负-1平0的节点集s,节点从0开始编号static void solve(vector<int>&out, map<int, vector<int>> &rg, map<int, int>&s){vector<int>visit(out.size());vector<int>v1, v2;for (auto si : s) {visit[si.first] = 1;if (si.second == -1)v1.push_back(si.first);if (si.second == 1)v2.push_back(si.first);}while (!v1.empty() || !v2.empty())fresh(v1, v2, s, visit, rg,out);for (int i = 0; i < out.size(); i++)s[i];}
private:static void fresh(vector<int>&v1, vector<int>&v2, map<int, int>&s, vector<int>&visit, map<int, vector<int>> &rg, vector<int>&out) {for (auto x : v1) {for (auto x2 : rg[x])if (visit[x2] == 0) {s[x2] = 1, v2.push_back(x2), visit[x2] = 1;}}v1.clear();for (auto x : v2) {for (auto x2 : rg[x])if (visit[x2] == 0) {if (--out[x2] == 0)s[x2] = -1, v1.push_back(x2), visit[x2] = 1;}}v2.clear();}
};

力扣 913. 猫和老鼠

两位玩家分别扮演猫和老鼠,在一张 无向 图上进行游戏,两人轮流行动。

图的形式是:graph[a] 是一个列表,由满足 ab 是图中的一条边的所有节点 b 组成。

老鼠从节点 1 开始,第一个出发;猫从节点 2 开始,第二个出发。在节点 0 处有一个洞。

在每个玩家的行动中,他们 必须 沿着图中与所在当前位置连通的一条边移动。例如,如果老鼠在节点 1 ,那么它必须移动到 graph[1] 中的任一节点。

此外,猫无法移动到洞中(节点 0)。

然后,游戏在出现以下三种情形之一时结束:

  • 如果猫和老鼠出现在同一个节点,猫获胜。
  • 如果老鼠到达洞中,老鼠获胜。
  • 如果某一位置重复出现(即,玩家的位置和移动顺序都与上一次行动相同),游戏平局。

给你一张图 graph ,并假设两位玩家都都以最佳状态参与游戏:

  • 如果老鼠获胜,则返回 1
  • 如果猫获胜,则返回 2
  • 如果平局,则返回 0 。

 

示例 1:

输入:graph = [[2,5],[3],[0,4,5],[1,4,5],[2,3],[0,2,3]]
输出:0

示例 2:

输入:graph = [[1,3],[0],[3],[0,2]]
输出:1

提示:

  • 3 <= graph.length <= 50
  • 1 <= graph[i].length < graph.length
  • 0 <= graph[i][j] < graph.length
  • graph[i][j] != i
  • graph[i] 互不相同
  • 猫和老鼠在游戏中总是可以移动
class Solution {
public:int catMouseGame(vector<vector<int>>& graph) {map<int, map<int, map<int, int>>>mn;map<int, int>s;int id = 0;for (int m = 0; m < graph.size(); m++) {for (int c = 0; c < graph.size(); c++) {for (int t = 0; t <= 1; t++) {mn[m][c][t] = id;if (m == 0 || c == 0)s[id] = (t ? -1 : 1);else if (m == c)s[id] = (t ? 1 : -1);id++;}}}vector<int>out(id);map<int, vector<int>> rg;for (int m = 0; m < graph.size(); m++) {for (int c = 0; c < graph.size(); c++) {for (auto m2 : graph[m])out[mn[m][c][0]]++, rg[mn[m2][c][1]].push_back(mn[m][c][0]);for (auto c2 : graph[c])out[mn[m][c][1]]++, rg[mn[m][c2][0]].push_back(mn[m][c][1]);}}SG2::solve(out, rg, s);int ans = s[mn[1][2][0]];return ans == -1 ? 2 : ans;}
};

相关文章:

公开游戏、基于有向图的游戏

目录 〇&#xff0c;背景 一&#xff0c;公开游戏、策梅洛定理 1&#xff0c;公开游戏 2&#xff0c;策梅洛定理 二&#xff0c;有向图游戏 1&#xff0c;狭义有向图游戏 2&#xff0c;广义有向图游戏 3&#xff0c;狭义有向图游戏的SG数 4&#xff0c;Bash Game 力扣…...

CSS学习笔记05

CSS笔记05 定位 position CSS 属性position - 用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。position 有以下常用的属性值&#xff1a; position: static; - 默认值。指定元素使用正常的布局行为&am…...

Linux查看指定端口是否被占用

在Linux中&#xff0c;可以使用多种方法来检查一个特定端口&#xff08;例如3306&#xff0c;通常由MySQL使用&#xff09;是否被占用&#xff1a; 使用netstat命令: 如果系统中已安装了netstat&#xff0c;可以使用以下命令检查3306端口&#xff1a; netstat -tuln | grep 330…...

【Python 自动化】小说推文一键生成思路概述

最近看了一下小说推文成品软件的思路&#xff0c;发现可以完全迁移到我的 BookerAutoVideo 上面来。这篇短文里面&#xff0c;我试着分析一下整个推文视频生成的流程&#xff0c;以及简要阐述一下有什么工具。 整体流程是这样&#xff1a; 分句 原文是按照段落组织的&#xf…...

MySQL中的字符集与排序规则详解

在 MySQL 中&#xff0c;字符集&#xff08;Character Set&#xff09;用于确定可以在数据库中存储的字符集合&#xff0c;而排序规则&#xff08;Collation&#xff09;用于指定比较和排序字符串的规则。下面是关于 MySQL 中字符集和排序规则的一些详细信息&#xff1a; 字符集…...

Java中如何进行加锁??

笔者在上篇文章介绍了线程安全的问题&#xff0c;接下来本篇文章就是来讲解如何避免线程安全问题~~ 前言&#xff1a;创建两个线程&#xff0c;每个线程都实现对同一个变量count各自自增5W次&#xff0c;我们来看一下代码&#xff1a; class Counter{private int count0;publi…...

Pytorch3D多角度渲染.obj模型

3D理解在从自动驾驶汽车和自主机器人到虚拟现实和增强现实的众多应用中发挥着至关重要的作用。在过去的一年里&#xff0c;PyTorch3D已经成为一个越来越流行的开源框架&#xff0c;用于使用Python进行3D深度学习。值得庆幸的是&#xff0c;PyTorch3D 库背后的人员已经完成了实现…...

MyBatisPlus 基础Mapperr接口:增删改查

MyBatisPlus 基础Mapper接口&#xff1a;增删改查 插入一条数据 代码 Testpublic void insert() {User user new User();user.setId(6L);user.setName("张三");user.setAge(25);user.setEmail("zhangsanexample.com");userMapper.insert(user);}日志 数…...

计算机网络与技术——概述

&#x1f60a;计算机网络与技术——概述 &#x1f47b;前言&#x1f94f;信息时代下计算机网络的发展&#x1f30f;互联网概述&#x1f4e1;计算机网络基本概念&#x1f4e1;互联网发展三阶段&#x1f4e1;互联网的标准化 &#x1f30f;互联网的组成&#x1f4e1;互联网的边缘部…...

详解TCP/IP协议第三篇:通信数据在OSI通信模型的上下传输

文章目录 一:OSI通信模型间数据传输展示 二:应用层到会话层解析 1:应用层 2:表现层 3:会话层...

《C++ primer plus》精炼(OOP部分)——对象和类(2)

“学习是人类成长的喷泉。” - 亚里士多德 文章目录 内联函数对象的方法和属性构造函数和析构函数构造函数的种类使用构造函数析构函数列表初始化 const成员函数this指针对象数组类作用域作用域为类的常量类作用域内的枚举 内联函数 定义位于类声明中的函数自动成为内联函数。…...

一点感受

做了两天企业数字化转型的评委&#xff0c;涉及全国最顶级的公司、最顶级的实际落地项目案例&#xff0c;由企业真实的落地团队亲自当面讲解。主要是为了了解了解真实的一线、真实的客户、真实的应用现状和应用水平。 &#xff08;1&#xff09;现状 我评审的涉及底层技术平台&…...

VirtualBox RockyLinux9 网络连接

有几次都是隔一段时间之后启动虚拟机&#xff0c;用第三方ssh工具就连接不上了。 简单记录一下。 1、VirtualBox设置 2、IP设置 cd /etc/NetworkManager/system-connections/ vim enp0s3.nmconnection[connection] idenp0s3 uuid9c404b41-4636-397c-8feb-5c2ed38ef404 typeet…...

java 实现适配器模式

适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将一个类的接口转换成另一个类的接口&#xff0c;使得原本不兼容的类可以协同工作。适配器模式包括两种类型&#xff1a;类适配器和对象适配器。下面分别介绍这两种类型的实现方式。 类…...

后端常用的Linux命令大全

后端常用的Linux命令大全 基础常用命令 Sudo Command 该命令是“superuser do”的缩写。sudo 是最常用的命令之一&#xff0c;可让你执行需要管理或 root 特权和权限的任务。 使用sudo命令时系统会提示用户重新使用密码进行身份验证。接下来&#xff0c;Linux 系统将记录一…...

C++面向对象

C面向对象知识 内存字节对齐 #pragma pack(n) 表示的是设置n字节对齐,windows默认是8字节&#xff0c;linux是4字节&#xff0c;鲲鹏是4字节 struct A{char a;int b;short c; };char占一个字节&#xff0c;起始偏移为零&#xff0c;int占四个字节&#xff0c;min(8,4)4&#x…...

什么是栈顶缓存技术

假设有一个基于流水线架构的处理器&#xff0c;它需要执行一系列指令。这些指令包括加载数据、执行计算和存储结果。在流水线中&#xff0c;不同阶段的指令可以并行执行。 现在考虑一个简单的情况&#xff0c;其中需要执行以下两个指令&#xff1a; 加载数据指令&#xff1a;…...

TDesign的input标签

目录 一、 新建页面01-todolist 二、 t-input标签、t-button标签 2.1 t-input标签 2.1.1 01-todolist.wxml页面 2.2 01-todolist.json页面 2.3 01-todolist.js页面 2.4 01-todolist.wxss页面 2.2 t-button标签 示例1&#xff1a;bind:change 示例2&#xff1a;bind:…...

从零开始学习 Java:简单易懂的入门指南之Map集合(二十三)

Map集合 1.Map集合1.1Map集合概述和特点1.2Map集合的基本功能1.3Map集合的获取功能1.4Map集合的遍历(方式1)1.5Map集合的遍历(方式2) 2.HashMap集合2.1HashMap集合概述和特点2.2HashMap集合应用案例 3.TreeMap集合3.1TreeMap集合概述和特点3.2TreeMap集合应用案例 1.Map集合 1…...

SpringBoot 拦截org.thymeleaf.exceptions.TemplateInputException异常

SpringBoot 拦截thymeleaf异常 org.thymeleaf.exceptions.TemplateInputException异常 org.thymeleaf.exceptions.TemplateProcessingE xception: Could not parse as each: "message : xxx " (template: “xxxx” - line xx, col xx) thymeleaf异常复现 你是故意的…...

Qt之随机数

介绍使用qsrand和qrand生成随机数。 生成随机数 生成随机数主要用到了函数qsrand和qrand&#xff0c;qsrand用来设置种子点&#xff0c;该种子为qrand生成随机数的起始值。如果不调用qsrand,那么qrand()就会自动调用qsrand(1)&#xff0c;即系统默认将1作为随机数的起始值。使…...

UWB学习——day2

UWB应用 基于上文UWB学习——day1中对UWB技术的相关优势介绍&#xff0c;UWB技术可广泛应用于以下场景。 WPAN&#xff08;无线个域网&#xff09; 基于其高精度&#xff08;亚厘米级&#xff09;、低功耗和高穿透性等特征&#xff0c;在以人为基础的个域网中应用广泛&#…...

使用 multiprocessing 多进程处理批量数据

示例代码 import multiprocessingdef process_data(data):# 这里是处理单个数据的过程return data * 2# 待处理的数据 data [1, 2, 3, 4, 5]def normal_func():# 普通处理方式result []for obj in data:result.append(process_data(obj)return resultdef parallel_func():# …...

React 与 TS 结合使用时组件传参总结

在学习 React 时&#xff0c;我们总会遇到在 TS 和 JS 之间切换来开发多个项目&#xff0c;而有时会忘记 TS 的语法&#xff0c;所以编写一下 React 结合 TS 开发时的一些总结知识点&#xff0c;以便后续回顾用。 向组件传递基础参数&#xff08;字符串、数字和布尔值&#xf…...

性能炸裂c++20协程+iocp/epoll,超轻量高性能异步库开发实战

前言&#xff1a; c20出来有一段时间了。其中一大功能就是终于支持协程了&#xff08;c作为行业大哥大级别的语言&#xff0c;居然到C20才开始支持协程&#xff0c;我也是无力吐槽了&#xff0c;让多少人等了多少年&#xff0c;等了多少青春&#xff09;但千呼万唤他终于还是来…...

自定义Dynamics 365实施和发布业务解决方案 - 4. 自动化业务流程

本章的主要重点是研究拟议应用程序的关键业务流程的自动化。每个组织每天都有自己独特的业务操作,这些操作是业务的关键部分。有些自动化的业务流程不需要用户交互,有些流程需要用户交互。此外,在某些业务流程中,某些用户操作完成,然后触发自动化流程来完成业务流程。 Dy…...

Lua03——开发环境搭建

1 安装开发插件 在 idea 或 vscode 中安装 lua 的开发插件 EmmyLua 2 创建工程 在 idea 中创建一个新的工程 工程的类型选择 lua 输入工程名及目标目录 在工程结构的SDK中设置lua在本地安装目录 在工程结构的modules中选择 lua 3 编写第一个lua程序 在工程下添加程序包&#…...

Redis 非关系型数据库 配置与优化

关系数据库与非关系型数据库 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#x…...

docker笔记8:Docker网络

1.是什么 1.1 docker不启动&#xff0c;默认网络情况 ens33 lo virbr0 在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后&#xff0c;启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡&#xff1a;它还有一个固定的默认IP地址192.168.122…...

C# 共享项目的应用

概述 共享项目也可以称为共享资产项目,它允许在多个目标项目之间共享的代码。 它支持编译器指令,可以有条件地包含特定于平台的代码,以便编译为引用共享项目的项目的子集。 还有 IDE 支持,可帮助管理编译器指令并直观显示代码在每个应用程序中的外观。 什么是共享项目? …...

政务网站队伍建设情况汇报/小网站

Vue.js 一、Vue基础知识 Vue概述 目标&#xff1a;MVVM模式应用特点&#xff0c;Vue概念 小结&#xff1a; MVVM通过视图与模型的双向绑定&#xff0c;简化前端操作。Vue是一款前端渐进式框架&#xff0c;能提高前端开发效率。 搭建示例工程 目标&#xff1a;使用HBuild…...

扬中做网站/网站有吗免费的

相信大家开始玩gitlabjenkins的时候对着两个工具有肯定有一定了解&#xff0c;我就不做详细解释了&#xff0c;下面就跟大家简单的说下gitlab&#xff0c;jenkins之间工作关系&#xff1a; GitLab是一个代码仓库&#xff0c;用来管理代码。Jenkins是一个自动化服务器&#xff0…...

深圳网站设计公司哪个/计算机培训

目录 一、导论 1 机器学习 寻找一种函数 2 如何寻找这个函数 3 学习路线 3.1 监督学习 3.2 半监督学习&#xff08;Semi-Supervised learning&#xff09; 3.3 迁移学习&#xff08;Transfer learning) 3.3 非监督学习(Unsupervised learning) 3.4 结构化学习(Structe…...

温岭哪里有做网站的/网站内部seo

SUSE的zypper本地源配置起来跟yum的配置很相似&#xff0c;它们的配置文件有很多相似之处。不过&#xff0c;个人觉得zypper这个工具稍微强大些。在SUSE下&#xff0c;可以通过一条zypper的命令&#xff0c;即可完成zypper源的配置。一、zypper源配置我这里内部搭建了一台源服务…...

如何查看一个网站做的外链/百度快速收录入口

NSPredicate的用法 http://www.cnblogs.com/MarsGG/articles/1949239.html 一般来说这种情况还是蛮多的&#xff0c;比如你从文件中读入了一个array1&#xff0c;然后想把程序中的一个array2中符合array1中内容的元素过滤出来。 正 常傻瓜一点就是两个for循环&#xff0c;一个…...

wordpress改变域名/深圳做网站的公司

环境及工具 手机 &#xff1a;小米手机 MI 2A 系统版本: Android 4.1.1 工具 : IDA pro 6.6 、C32Asm 、VS2005 一&#xff1a;第一次打开加密视频会出现如下验证: 输入用户名与密码登录成功后如下图 点击“支付并获取许可证”成功后就可以播放加密的视频了&#xff0c;并…...