题干要求:
给你一个下标从 0 开始的 8 x 8
网格 board
,其中 board[r][c]
表示游戏棋盘上的格子 (r, c)
。棋盘上空格用 '.'
表示,白色格子用 'W'
表示,黑色格子用 'B'
表示。
游戏中每次操作步骤为:选择一个空格子,将它变成你正在执行的颜色(要么白色,要么黑色)。但是,合法 操作必须满足:涂色后这个格子是 好线段的一个端点 (好线段可以是水平的,竖直的或者是对角线)。
好线段 指的是一个包含 三个或者更多格子(包含端点格子)的线段,线段两个端点格子为 同一种颜色 ,且中间剩余格子的颜色都为 另一种颜色 (线段上不能有任何空格子)。你可以在下图找到好线段的例子:
给你两个整数 rMove
和 cMove
以及一个字符 color
,表示你正在执行操作的颜色(白或者黑),如果将格子 (rMove, cMove)
变成颜色 color
后,是一个 合法 操作,那么返回 true
,如果不是合法操作返回 false
。
示例 1:
输入:board = [[".",".",".","B",".",".",".","."],[".",".",".","W",".",".",".","."],[".",".",".","W",".",".",".","."],[".",".",".","W",".",".",".","."],["W","B","B",".","W","W","W","B"],[".",".",".","B",".",".",".","."],[".",".",".","B",".",".",".","."],[".",".",".","W",".",".",".","."]], rMove = 4, cMove = 3, color = "B" 输出:true 解释:'.','W' 和 'B' 分别用颜色蓝色,白色和黑色表示。格子 (rMove, cMove) 用 'X' 标记。 以选中格子为端点的两个好线段在上图中用红色矩形标注出来了。
示例 2:
输入:board = [[".",".",".",".",".",".",".","."],[".","B",".",".","W",".",".","."],[".",".","W",".",".",".",".","."],[".",".",".","W","B",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".","B","W",".","."],[".",".",".",".",".",".","W","."],[".",".",".",".",".",".",".","B"]], rMove = 4, cMove = 4, color = "W" 输出:false 解释:虽然选中格子涂色后,棋盘上产生了好线段,但选中格子是作为中间格子,没有产生以选中格子为端点的好线段。
提示:
board.length == board[r].length == 8
0 <= rMove, cMove < 8
board[rMove][cMove] == '.'
color
要么是'B'
要么是'W'
。
题干分析:
首先我们先要理解题干的含义:简单来说就是通过相关操作(选择一个空格子,将它变成我们正在执行的颜色(要么白色,要么黑色))构造满足构造准则的线段。这里的线段有着详细的要求:好线段 指的是一个包含 三个或者更多格子(包含端点格子)的线段,线段两个端点格子为 同一种颜色 ,且中间剩余格子的颜色都为 另一种颜色 (线段上不能有任何空格子)。
根据题干分析编写代码:
首先第一步:定义一个函数用于初始化棋盘
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZE 8
// 棋盘初始化函数,用于创建并初始化棋盘
char** initBoard() {
char** board = (char**)malloc(SIZE * sizeof(char*));//为棋盘分配内存
//将棋盘的各个位置先初始化为空格
for (int i = 0; i < SIZE; i++) {
board[i] = (char*)malloc(SIZE * sizeof(char));
for (int j = 0; j < SIZE; j++) {
board[i][j] = '.';
}
}
return board;
}
第二步:因为上述函数操作中包含有为初始化的棋盘分配内存的操作,因此我们必须定义一个函数用于释放此内存:
// 棋盘释放函数
void freeBoard(char** board) {
for (int i = 0; i < SIZE; i++) {
free(board[i]);
}
free(board);
}
第三步:既然最基本的函数操作已经定义完毕,这个时候我们就应该定义者其中最重要的方法之一:判断经过操作的线段是否满足好线段的要求,相关代码如下:
//定义函数用于检查一个方向上的线段是否合法
bool checkDirection(char** board, int rMove, int cMove, int dx, int dy, char color) {
int x = rMove + dx;
int y = cMove + dy;
int step = 1; // 当前遍历到的节点序号
while (x >= 0 && x < SIZE && y >= 0 && y < SIZE) {
if (step == 1) {
// 第一个点必须为相反颜色
if (board[x][y] == '.' || board[x][y] == color) {
return false;
}
}
else {
// 好线段中不应存在空格子
if (board[x][y] == '.') {
return false;
}
// 遍历到好线段的终点,返回 true
if (board[x][y] == color) {
return true;
}
}
step++;
x += dx;
y += dy;
}
// 不存在符合要求的好线段
return false;
}
以上代码的基本思路如下:
首先定义一个节点序号用于遍历好线段并判断此“好线段”的构造是否符合要求 。已知好线段要求在已知端点颜色的情况下(step = 0),下一节点序号(step = 1)的颜色必须相反,否则不满足好线段的构造要求(return false);除此之外我们还要求好线段之间不可以存在空格否则不满足好线段的构造要求(return false);最后当我们遍历到好线段的终点需要返回true。
第四步:最重要的方法之二:定义构造好线段的增量数组用于增长线段
bool checkMove(char** board, int boardSize, int boardColSize, int rMove, int cMove, char color) {
// 定义八个方向的增量数组
int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; // 行改变量
int dy[8] = { 0, 1, 1, 1, 0, -1, -1, -1 }; // 列改变量
// 从 x 轴正方向开始逆时针枚举 8 个方向
for (int k = 0; k < 8; ++k) {
// 调用checkDirection函数来检查每个方向
if (checkDirection(board, rMove, cMove, dx[k], dy[k], color)) {
return true;
}
}
return false;
}
这个函数 checkMove
用于检查在指定位置(rMove
, cMove
)放置特定颜色的棋子(color
)是否是一个合法的操作。具体来说,它会检查在这个位置放置棋子后是否会形成一个符合游戏规则的“好线段”。
其相关的代码执行原理如下:
- 使用
for
循环遍历所有八个方向。从k = 0
到k = 7
,依次检查每个方向。 - 对于每个方向,使用
dx[k]
和dy[k]
来获取该方向上的行和列增量。
调用 checkDirection
函数:
checkDirection
函数用于检查在某个方向上是否存在一个符合规则的线段。- 将当前方向的增量
dx[k]
和dy[k]
传递给checkDirection
,并检查从rMove
和cMove
开始的该方向上的线段。 - 如果
checkDirection
返回true
,则表示在这个方向上有一个符合规则的线段,因此整个函数返回true
,表示在指定位置放置棋子是一个合法操作。
如果所有方向都检查完毕后都没有发现符合规则的线段,则返回 false
,表示在指定位置放置棋子不是一个合法操作。
最后定义一个测试函数用来测试代码编写是否符合需求:
// 测试函数
int main() {
// 初始化棋盘
char** board = initBoard();
board[3][3] = 'W';
board[3][4] = 'B';
board[3][5] = 'W';
board[4][3] = 'B';
board[4][4] = 'W';
board[4][5] = 'B';
int rMove = 4; // 测试的行位置
int cMove = 2; // 测试的列位置
char color = 'W'; // 要放置的颜色
int boardColSize = SIZE; // 列数
// 检查在指定位置放置颜色为color的棋子是否合法
if (checkMove(board, SIZE, boardColSize, rMove, cMove, color)) {
printf("合法操作\n");
}
else {
printf("非法操作\n");
}
// 释放棋盘内存
freeBoard(board);
return 0;
}
测试结果:
已知题干最后仅需要编写关键函数即可,因此在力扣代码编写上我们只需编写checkDirection函数以及checkMove函数即可,测试结果如下: