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

目录

〇,背景

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

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;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/74095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVGL Animations(动画)的简单使用

一、前言 哈喽&#xff0c;大家好。在进行界面设计的时候&#xff0c;动画的使用是必不可少的&#xff0c;今天这篇文章就跟大家分享一下 LVGL Animations&#xff08;动画&#xff09;的简单使用。笔者将在模拟器上运行演示&#xff0c;LVGL 版本号为 8.3.0。 二、Animation…

【实践篇】Redis最强Java客户端(一)之Redisson入门介绍

Redisson入门介绍 文章目录 Redisson入门介绍1.1 Redisson简介1.1.1 起源和历史1.1.2 优势和特点1.1.3 与其他Java Redis客户端的比较 1.2 使用和配置1.2.1 依赖和SDK1.2.2 配置文件解析1.2.3 连接池配置 1.3 优雅的让Hash的某个Field过期2. 参考资料3. 源码地址4. Redis从入门…

JAVASE---抽象类和接口

抽象类 抽象类的概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。 抽象类语法 在…

反编译 SyntaxError: Unexpected token ‘}‘ 异常处理

反编译小程序出现异常&#xff1a; SyntaxError: Unexpected token ‘}’ 网上很多都说使用最新版本的反编译 wxappUnpacker-master 包可以进行解析&#xff0c;但是大神已经停止了更新wxappUnpacker-master 包&#xff1b; 查找了网上大部分的wxappUnpacker-master 包&#…

BBR 带宽估计的延后累加

一个关于时延统计分布的小测试&#xff0c;用 netem delay jitter distribution pareto 模拟&#xff0c;得到下面的结果&#xff1a; netem 的 jitter 并不是真 jitter&#xff0c;只是通过延时阻滞部分报文模拟 jitter&#xff0c;对保序流而言&#xff0c;就表现为乱序&am…

strlen函数使用与模拟实现【进阶版】

strlen函数使用与模拟实现 1.strlen函数介绍 资源来源于cplusplus网站 翻译过来的大致意思就是&#xff1a; 获取字符串长度 2.strlen的使用 int main() { //strlen - 求字符串长度的 //字符串的结束标志是\0 //strlen统计的是\0之前出现的字符的个数 //基本功能 char arr[]…

华为数通方向HCIP-DataCom H12-821题库(单选题:321-340)

第321题 BGP的Open报文是用于建立对等体连接的,以下哪一项不属于Open报文中携带的参数信息? A、发送者的Router ID B、AS号 C、BGP版本号 D、TCP端口号 答案:D 解析:以下是BGP的Open报文: 第322题 在建立BGP对等体的过程中,OpenSent状态表明BGP等待的Open报文 并对收…

NetSuite 客户销售团队更新方法辨析

在NetSuite中如果想将销售团队与客户数据进行隔离&#xff0c;采用的方法是“在客户上定义销售团队&#xff0c;同时将销售团队成员的访问角色进行Employee Restrictions”。 其中&#xff0c;Employee Restrictions的主题我们过去发过几篇&#xff0c;大家可以参考。 NetSui…

Spring Reactive:响应式编程与WebFlux的深度探索

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

C# 共享项目的应用

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

记一起小意外事件引起的批量重命名文件名

一、事件描述 某次,因某业务系统迁移,一线人员对业务目录误操作,执行打包命令过程中导致Tomcat下的web应用程序无法使用,检查后发现项目下所有文件名都加了gz格式;询问一线,发现是对项目目录执行了:gzip -r ./tomcat导致程序文件找不到;报错如下: 二、事件处理 1、查看…

c语言开篇---跟着视频学C语言

标识符 标识符必须声明定义&#xff0c;可以是变量、函数或其他实体。 Int是标识符吗&#xff1f; 不是&#xff0c;int是c语言关键词&#xff0c;不是随意命名的 C语言关键词如下&#xff1a; 常量 不需要被声明&#xff0c;不能赋值更改。 printf函数 printf是由print打印…

对Transformer中的Attention(注意力机制)的一点点探索

摘要&#xff1a;本文试图对 Transformer 中的 Attention 机制进行一点点探索。并就 6 个问题深入展开。 ✅ NLP 研 1 选手的学习笔记 简介&#xff1a;小王&#xff0c;NPU&#xff0c;2023级&#xff0c;计算机技术 研究方向&#xff1a;文本生成、摘要生成 文章目录 一、为啥…

python创建exe文件

1、搭建环境 pip install pyinstaller 2、准备测试代码 exe_test.py import timeprint("hello") print("hello") print("hello") print("hello")time.sleep(5) 注&#xff1a;添加sleep以便在执行exe文件的时候能看到结果 3、生…

linux-OpenSSL升级

1.安装编译所需的安装包 yum install -y gcc make perl zlib-devel 2.从 OpenSSL 官网下载&#xff08;https://www.openssl.org/source/openssl-1.1.1v.tar.gz&#xff09; 注:如果原先版本为1.x.x,升级时还是需要选择1.x.x 3. 编译安装 # 解压tar -xvf openssl-1.1.1v.tar…

[Linux]进程间通信--管道

[Linux]进程间通信–管道 文章目录 [Linux]进程间通信--管道进程间通信的目的实现进程间通信的原理匿名管道匿名管道的通信原理系统接口管道特性管道的协同场景管道的大小 命名管道使用指令创建命名管道使用系统调用创建命名管道 进程间通信的目的 数据传输&#xff1a;一个进…

docker笔记7:Docker微服务实战

1.通过IDEA新建一个普通微服务模块 建Module docker_boot 改POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi…

【Maven教程】(四)坐标与依赖:坐标概念,依赖配置、范围、传递性和最佳实践 ~

Maven 坐标与依赖 1️⃣ 什么是Maven 坐标2️⃣ 坐标详解3️⃣ 依赖的配置4️⃣ 依赖范围5️⃣ 传递性依赖6️⃣ 依赖调解7️⃣ 可选依赖8️⃣ 最佳实践8.1 排除依赖8.2 归类依赖8.3 优化依赖 &#x1f33e; 总结 正如前面文章所述&#xff0c;Maven 的一大功能是管理项目依赖…

Spring Boot与Kubernetes:现代云部署的完美组合

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Python实操 PDF自动识别并提取Excel文件

最近几天&#xff0c;paddleOCR开发了新的功能&#xff0c;通过将图片中的表格提取出来&#xff0c;效果还不错&#xff0c;今天&#xff0c;作者按照步骤测试了一波。 首先&#xff0c;讲下这个工具是干什么用的&#xff1a;它的功能主要是针对一张完整的PDF图片&#xff0c;可…