拓展三字棋
题目描述
在 3×3 的棋盘玩三子棋太无聊了,小 A 拉了很多同学一起来玩三子棋,同时棋盘变成了 n×n 的正方形棋盘。
三子棋的规则是每位玩家轮流写下一个字母,同一名玩家的字母相同。当有一名玩家在行、列或者斜线上连续组成了 3 个自己的字母时,即为获胜。
给定棋盘的当前状态,请找出获胜的玩家。
输入格式
输入第一行为一个整数 n。
接下来的 n 行,每行 n 个字符,可能为大写字母或 .
(表示没人在这下棋)。
输出格式
输出一行一个大小字母,表示获胜的玩家所写的那种字母。
如果没有人获胜,则输出 ongoing
。
数据保证最多只有一个人获胜。
输入输出样例
输入 #1
3 XOC XOC X..
输出 #1
X
输入 #2
4 .... ..A. AAB. .B.B
输出 #2
ongoing
输入 #3
3 ABB AAA BBA
输出 #3
A
说明/提示
对于 100% 的数据,保证 1≤n≤30。
分析题目
这道题的关键在于检查棋盘上是否存在连续三个相同的非空字符(X 或 O)。
检查所有可能的行、列、对角线是否存在连续三个相同的字符
具体要怎么实现呢?
- 遍历棋盘:对每个位置
(i, j)
检查是否为有效字符 - 检查四个方向:
- 水平方向:检查
(i, j)
右侧两个位置 - 垂直方向:检查
(i, j)
下方两个位置 - 主对角线方向:检查右下方向两个位置
- 副对角线方向:检查左下方向两个位置
- 水平方向:检查
- 边界条件(很重要!!):确保遍历的位置不越界
这个思路的关键在于通过双重循环遍历棋盘的每个位置,并在每个位置上检查四个方向的连续性,确保能够覆盖所有的获胜情况。
那根据这个思路,可以写出初步代码
#include <bits/stdc++.h>
using namespace std;
char win(char a[][35],int n)
{for(int i=0;i<n;i++){if(a[i][0]!='.' && a[i][0]==a[i][1] && a[i][1]==a[i][2]){return a[i][0];}}for(int j=0;j<n;j++){if(a[0][j]!='.' && a[0][j]==a[1][j] && a[1][j]==a[2][j]){return a[0][j];}}if(a[0][0]!='.' && a[0][0]==a[1][1] && a[1][1]==a[2][2]){return a[0][0];}if(a[0][2]!='.' && a[0][2]==a[1][1] && a[1][1]==a[2][0]){return a[0][2];}return ' ';
}
int main()
{int n;cin>>n;char a[35][35];for(int i=0;i<n;i++){string row;cin>>row;for(int j=0;j<n;j++){a[i][j]=row[j];}}char w=win(a,n);if(w!=' '){cout<<w;}else{cout<<"ongoing";}return 0;
}
但是提交之后就会发现,这个代码只得了30分
这是为什么呢?
错误代码分析
仔细观察可以发现,这个代码没有符合题目给出的数据范围条件
在win
函数里,仅仅对 n = 3
的情况进行了判断,也就是只检查了固定的行(每一行的第 0、1、2 列)、列(每一列的第 0、1、2 行 )以及两条斜线(主对角线 a[0][0]
、a[1][1]
、a[2][2]
和副对角线 a[0][2]
、a[1][1]
、a[2][0]
)是否有连续三个相同且非 .
的字符。
但题目中 n
是可以变化的(范围 1 ≤ n ≤ 30
),当 n > 3
时,这个代码便无法判断其他存在连续三个相同字符的行、列、斜线情况。
正确代码
通过遍历每个位置,并在每个位置上动态地检查行、列、斜线方向是否有连续三个相同字符的方式
以下是实现的步骤
首先最重要的是遍历棋盘的每一个位置,当遇到非 .
的字符时,从该位置出发,去检查其所在的行、列、两条斜线方向上,是否存在连续三个相同的字符。
接下来是关于行,列,对角线的判断:
对于行方向,要检查当前位置向右连续两个位置(即 j + 2 < n
时,判断 a[i][j]
、a[i][j + 1]
、a[i][j + 2]
是否相同且非 .
)。
对于列方向,要检查当前位置向下连续两个位置(即 i + 2 < n
时,判断 a[i][j]
、a[i + 1][j]
、a[i + 2][j]
是否相同且非 .
)。
对于主对角线方向(从左上到右下 ),要检查当前位置向右下连续两个位置(即 i + 2 < n
且 j + 2 < n
时,判断 a[i][j]
、a[i + 1][j + 1]
、a[i + 2][j + 2]
是否相同且非 .
)。
对于副对角线方向(从右上到左下 ),要检查当前位置向左下连续两个位置(即 i + 2 < n
且 j - 2 >= 0
时,判断 a[i][j]
、a[i + 1][j - 1]
、a[i + 2][j - 2]
是否相同且非 .
)。
最后是胜负判断。如果在任何一种检查中发现了连续三个相同且非 .
的字符,就返回对应的字符,表示该玩家获胜;
如果遍历完整个棋盘都没有找到这样的情况,就返回 ' '
主函数中再根据返回值判断输出获胜字符还是 ongoing
。
#include <bits/stdc++.h>
using namespace std;char win(char a[][35], int n) {for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (a[i][j] == '.') {continue; }if (j + 2 < n && a[i][j] == a[i][j + 1] && a[i][j + 1] == a[i][j + 2]) {return a[i][j];}if (i + 2 < n && a[i][j] == a[i + 1][j] && a[i + 1][j] == a[i + 2][j]) {return a[i][j];}if (i + 2 < n && j + 2 < n && a[i][j] == a[i + 1][j + 1] && a[i + 1][j + 1] == a[i + 2][j + 2]) {return a[i][j];}if (i + 2 < n && j - 2 >= 0 && a[i][j] == a[i + 1][j - 1] && a[i + 1][j - 1] == a[i + 2][j - 2]) {return a[i][j];}}}return ' ';
}int main() {int n;cin >> n;char a[35][35];for (int i = 0; i < n; i++) {string row;cin >> row;for (int j = 0; j < n; j++) {a[i][j] = row[j];}}char w = win(a, n);if (w != ' ') {cout << w;} else {cout << "ongoing";}return 0;
}