A和B玩游戏如下:
对于由矩阵表示的迷宫,其大小为 n × n ,则单元格包含字符“”。表示可移动区域,单元格包含字符“#”。字符表示无法踩到的障碍物。从单元格( n , n )开始,每个人轮流选择要移动的下一个位置。新位置必须在当前单元格的左侧或上方(作为国际象棋中的车子移动)。当前位置和新位置之间没有障碍。如果找不到新职位,该人将失去。 A是第一个走的人。确定谁是赢家。
例如:
. .
. .
结果为B,因为最初在坐标(2,2)处,A将向左或向上移动,因此将有坐标(1,2)或(2,1)。然后B将移动到坐标(1,1)。 A不能再移动了,所以他输了,B赢了。
. . # . .
# . . . #
. . # . .
# . . . .
. . . . .
类似地解释,我们有A将成为赢家。
这是我的尝试:我尝试使用递归编程来确定谁是赢家,但是当 n 很大时,它花费的时间太长,为此我尝试构建动态编程。问题。
编辑:
所以,这是我们如何确定谁是这场比赛的胜利者的主要问题。假设最初在坐标( n , n )处有一块石头。 A和B轮流玩游戏,如下所示:A将选择一个新位置来移动该石头(我们可以想象该石头像国际象棋中的车子一样),这个新位置必须在当前单元格的左侧或上方,之后,B也选择一个新位置来移动此石头。直到无法移动这块石头的人成为失败者。
请注意:字符“。”代表可移动土地,而字符“#”代表障碍!
发布此问题的目的是,我想尝试动态编程或递归以确定谁是这场比赛的赢家。
答案 0 :(得分:2)
我们可以将矩阵中的坐标分类为获胜(即,在正确比赛中获胜的一方获胜)或失败(即在正确比赛中获胜的一方获胜)
对应于可移动土地的坐标(r,c)
是
请注意,根据第一个规则,(1,1)输了,因此,根据最后一个规则,可以将石头移至(1,1)的任何人都将获胜。
第二个矩阵的分类是:
L W # L W
# L W W #
L W # W L
# W L W W
L W W W W
由于坐标的值仅取决于左右的值,因此我们可以按从上到下,从左到右的顺序计算值。您实际上不需要递归或动态编程。像
for r in 1...n
for c in 1...n
look left and up until the edge of the board or the first # sign
if there are any L values, then mark (r,c) as winning
otherwise mark (r,c) as losing
这种幼稚的方法每个坐标花费O(n)时间,因此总时间为O(n 3 )。可以通过在扫描矩阵时保留一些布尔标志来将其改进为O(n 2 ):
答案 1 :(得分:2)
好吧,您只需申请Sprague-Grundy theorem即可找出获胜者。
因此计算粗略数字将是这样的:
0 . # 0 .
# . . . #
0 . # . .
# . . . .
0 . . . .
.
)一举找到所有可到达的单元格0 1 # 0 1
# 0 1 2 #
0 2 # 1 0
# 3 0 4 1
0 4 1 3 2
示例代码(C ++),O(n ^ 3):
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<string>A = {
"..#..",
"#...#",
"..#..",
"#....",
"....."};
int n = A.size();
int Grundy[n][n]={};
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
if(A[i][j]!='#')
{
int can[2*n+1]={};
int left = j-1;
while(left>=0 && A[i][left]!='#')
{
can[Grundy[i][left]]++;
left--;
}
int up = i-1;
while(up>=0 && A[up][j]!='#')
{
can[Grundy[up][j]]++;
up--;
}
while(can[Grundy[i][j]])Grundy[i][j]++;
}
cout<<(Grundy[n-1][n-1] ? "Player 1 wins\n" : "Player 2 wins\n");
}
这将产生O(n ^ 3)解,尽管我们仍然可以按以下方式优化为O(n ^ 2):
win[i][j]
-存储是否可以从(i,j)单元中获胜loseRow[i][j]
-存储第i行中是否有可以从像元(i,j)到达的丢失像元loseCol[i][j]
-存储是否可以从单元格(i,j)到达列j中是否有丢失的单元格示例代码(C ++),O(n ^ 2):
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<string>A = {
"..#..",
"#...#",
"..#..",
"#....",
"....."};
int n = A.size();
int win[n][n]={};
int loseRow[n][n]={};
int loseCol[n][n]={};
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
if(A[i][j]!='#')
{
if(j-1>=0 && A[i][j-1]!='#')
{
win[i][j]|=loseRow[i][j-1];
loseRow[i][j]=loseRow[i][j-1];
}
if(i-1>=0 && A[i-1][j]!='#')
{
win[i][j]|=loseCol[i-1][j];
loseCol[i][j]=loseCol[i-1][j];
}
loseRow[i][j]|=!win[i][j];
loseCol[i][j]|=!win[i][j];
}
cout<<(win[n-1][n-1] ? "Player 1 wins\n" : "Player 2 wins\n");
}