【题解】五子棋
题面
luogu 找不到原题,把 OJ 上的贴过来力!
前言
原来云落打暴力也会挂分……
正文
离线做法!
当棋局没有结束,并且触发了某种特殊条件(如一方获胜,或不合法的落子),棋局会立即终止
在代码表示中,直接 return 0 会终止整个进程,会让数据无法完全读入……
所以需要记录每次落子,即离线做法
正片开始!
棋盘用一个二维数组 $ mp $ 维护,初始化为 $ -1 $,表示棋盘为空
对于每次落子,模拟如下操作:
我们记 $ x,y,col(0/1) $ 分别表示当前落子的横坐标、纵坐标、颜色(白/黑)
首先,对 “Wrong” 的情况进行判定
-
出界(inbound 函数直接判 $ x,y $ 即可)
-
落子处不为空,即 $ mp_{x,y} \neq -1 $
其次,获胜判定
先上一个结论:如果落子之前一方没有获胜且落子后获胜,则落子必然在获胜的连续五子中
考虑八联通搜索——枚举方向,步数
记录一个 $ cnt $ 数组,表示四个方向(八联通二合一)可拓展的最远距离
更新 $ cnt $ 后直接判断是否存在 $ j $ 使得 $ cnt_j \ge 5 $ 即可
注意事项:
-
$ mp $ 初始化赋值 $ -1 $
-
方向数组八联通
-
数组大小需要充分(云落因此虚空调试 24h)
-
$ cnt $ 数组初始化为 $ 1 $(想想为什么捏?)
-
注意输出的文末“!”
代码
没有删除调试代码,但似乎有辅助理解的作用?
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=128,maxm=2048;
const int dx[]={0,1,1,1,0,-1,-1,-1},dy[]={1,1,0,-1,-1,-1,0,1};
int n,m,mp[maxn][maxn],xx[maxm],yy[maxm];
inline bool inbound(int x,int y){
return x>=1&&x<=n&&y>=1&&y<=n;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>xx[i]>>yy[i];
}
memset(mp,-1,sizeof(mp));
for(int i=1;i<=m;i++){
int x=xx[i],y=yy[i],col=i&1;//1表示黑,0表示白
// cout<<x<<' '<<y<<' '<<col<<endl;
if(!inbound(x,y)||mp[x][y]!=-1){
cout<<"Wrong!"<<endl;
return 0;
}
mp[x][y]=col;
// cout<<i<<endl;
// for(int r=1;r<=n;r++){
// for(int c=1;c<=n;c++){
// cout<<mp[r][c]<<' ';
// }
// cout<<endl;
// }
int cnt[4]={1,1,1,1};
for(int k=0;k<8;k++){
for(int s=1;s<=5;s++){
int nx=x+dx[k]*s,ny=y+dy[k]*s;
//cout<<nx<<' '<<ny<<endl;
if(!inbound(nx,ny)||mp[nx][ny]!=col){
break;
}
cnt[k%4]++;
}
}
for(int j=0;j<4;j++){
//cout<<"CNT"<<j<<":"<<cnt[j]<<endl;
if(cnt[j]>=5){
if(col==1){
cout<<"PYB wins!"<<endl;
}else{
cout<<"YYX wins!"<<endl;
}
return 0;
}
}
}
cout<<"Draw!"<<endl;
return 0;
}
后记
云落下班!
完结撒花