【问题描述】
井字棋,是一种在3×3格子上进行的连珠游戏,和五子棋比较类似,由于棋盘一般不画边框,格线排成井字故得名。游戏需要的工具仅为纸和笔,然后由分别代表O和X的两个游戏者轮流在格子里留下标记井字策略。任意三个标记形成一条直线,则为获胜。
Alice和Bob考完了期末考试,并获得了100分的好成绩,正觉无聊,于是开始玩起了井字棋。
给出 n 个3×3的字符数组,用于表示 n 次对局最终的标记情况。
由于总是一个人先手会使得游戏趣味性降低,每一局的先手者并不是固定的。
具体给出一个长度为 n 的字符串s,s的第 i 个字符表示第 i 场对局的先手者是谁(0≤ i <n),‘A’表示Alice先手,‘B’表示Bob先手。
先手者执“1”棋子,后手者执“2”棋子,无棋子处用“0”表示。
请分别判断这个 n 次对局的获胜者。
题目数据保证所有的局面都是合法且可判断唯一获胜者的。
【输入形式】
输入的第一行包含一个正整数 n ( n ≤300),表示对局的次数。
输入的第二行包含一个字符串s,表示各对局的先手情况。
接下来3 * n 行,每行由一个长度为3的字符串组成,每3行共9个字符对应一个局面。
【输出形式】
输出共 n 行,每行输出A或B,表示每场对局的获胜者是谁。
【样例输入】
2 AB 120 112 201 222 101 010
【样例输出】
A A
【样例说明】
第一场对局中棋子1在正对角线上三个相连,而Alice先手,所以Alice获胜
第二场对局中棋子2在第一行上三个相连,而Bob先手,Alice后手,所以Alice获胜
注:
此为CCF系列题解--2018年3月第四题 井字棋游戏 变体
代码思路参考该大佬(8条消息) CCF系列题解--2018年3月第四题 井字棋游戏_Jing Sir的博客-CSDN博客
先来说下该题思路:
首先:该题相较于原题难度大大降低,给定的最终棋局为一固定棋局,因此不需要考虑之后选手下的棋局,只需对现在已经确定的棋局进行判定即可。
其次:本题多了一个先手后手问题
且注意到 “题目数据保证所有的局面都是合法且可判断唯一获胜者的。” 因此可以换种思路,考虑棋盘上的棋子数以及先手问题,但本问不对此过多赘述。主要讲解一下本体在原题基础上的一个较为普遍的通解。(其实主要是想写下我的离谱错误.........)
先来看下错误代码
#include<cstring>
#include<iostream>
using namespace std;
char a[3][3];
bool hok(int h,int f)//判断行
{
return a[h][0]==(f+'0')&&a[h][1]==(f+'0')&&a[h][2]==(f+'0');
}
bool lok(int l,int f)//判断列
{
return a[0][l]==(f+'0')&&a[1][l]==(f+'0')&&a[2][l]==(f+'0');
}
int win(int f)//判断胜利
{
if(hok(0,f)||hok(1,f)||hok(2,f))
return 1;
if(lok(0,f)||lok(1,f)||lok(2,f))
return 1;
if(a[0][0]==(f+'0')&&a[1][1]==(f+'0')&&a[2][2]==(f+'0'))
return 1;
if(a[2][0]==(f+'0')&&a[1][1]==(f+'0')&&a[0][2]==(f+'0'))
return 1;
return 0;
}
int main()
{
int n;
cin>>n;
string s;
cin>>s;
char a[3][3];
for(int i=0;i<n;i++)//n个结果
{
for(int j=0;j<3;j++)
{
string str;
cin>>str;
for(int k=0;k<3;k++)
{
a[j][k]=str[k];
}
}
int flag1=0;
int flag2=0;
if(s[i]=='A')
{
flag1=win(1);
if(flag1==1)
cout<<'A'<<endl;
else
cout<<'B'<<endl;
}
else
{
flag2=win(1);
if(flag2==1)
cout<<'B'<<endl;
else
cout<<'A'<<endl;
}
}
return 0;
}
一眼看过去好像啥都没错,但是实际跑出来全是错误。而且即使一条条调用函数,跑出来的全是0,但将函数体直接扔到main里面却能得到对的返回值。博主百思不得其解(浪费 long time)
然后找老师盯了半天,发现一个很小但致命的错误。
注意注意:
静态区的函数里面需要调用棋盘数组,则必须在静态区开一个棋盘数组。(即此处如果不开,只在main函数里开,则函数会报错)。
但是这段错误的代码离谱的一个小错误在于 main函数里又把棋盘数组a[3][3]开了一遍。导致main函数里的操作只在main函数里生效,静态区的a[3][3]值没有传递过去。
尤其是main函数前面写了不少函数,然后检查代码时,查完几个函数就对自己开的数组没印象了。(虽然感觉也没几个会像我这样离谱,但还是记录一下,留个警示吧)
正确代码
#include<cstring>
#include<iostream>
using namespace std;
char a[3][3];
bool hok(int h,int f)//判断行
{
return a[h][0]==(f+'0')&&a[h][1]==(f+'0')&&a[h][2]==(f+'0');
}
bool lok(int l,int f)//判断列
{
return a[0][l]==(f+'0')&&a[1][l]==(f+'0')&&a[2][l]==(f+'0');
}
int win(int f)//判断胜利
{
if(hok(0,f)||hok(1,f)||hok(2,f))
return 1;
if(lok(0,f)||lok(1,f)||lok(2,f))
return 1;
if(a[0][0]==(f+'0')&&a[1][1]==(f+'0')&&a[2][2]==(f+'0'))
return 1;
if(a[2][0]==(f+'0')&&a[1][1]==(f+'0')&&a[0][2]==(f+'0'))
return 1;
return 0;
}
int main()
{
int n;
cin>>n;
string s;
cin>>s;
for(int i=0;i<n;i++)//n个结果
{
for(int j=0;j<3;j++)
{
string str;
cin>>str;
for(int k=0;k<3;k++)
{
a[j][k]=str[k];
}
}
int flag1=0;
int flag2=0;
if(s[i]=='A')
{
flag1=win(1);
if(flag1==1)
cout<<'A'<<endl;
else
cout<<'B'<<endl;
}
else
{
flag2=win(1);
if(flag2==1)
cout<<'B'<<endl;
else
cout<<'A'<<endl;
}
}
return 0;
}
额外补充:
1.每个函数在任何情况下都要有对应返回值。void除外。(即 bool 函数不能只写返回true的,什么时候返回false也要写,以及main函数写return 0养成好习惯)
2.括号不要随便省略 a[i][j]!=b等价于 !(a[i][j]==b)(!a[i][j]==b是错的)
3.代码错了多角度思考思考,心平气和写(虽然不大可能),但是急起来确实越写越乱.....