【题解】五子棋

题面

luogu 找不到原题,把 OJ 上的贴过来力!

题目传送门

前言

原来云落打暴力也会挂分……

正文

离线做法!

当棋局没有结束,并且触发了某种特殊条件(如一方获胜,或不合法的落子),棋局会立即终止

在代码表示中,直接 return 0 会终止整个进程,会让数据无法完全读入……

所以需要记录每次落子,即离线做法

正片开始!

棋盘用一个二维数组 $ mp $ 维护,初始化为 $ -1 $,表示棋盘为空

对于每次落子,模拟如下操作:

我们记 $ x,y,col(0/1) $ 分别表示当前落子的横坐标、纵坐标、颜色(白/黑)

首先,对 “Wrong” 的情况进行判定

  1. 出界(inbound 函数直接判 $ x,y $ 即可)

  2. 落子处不为空,即 $ mp_{x,y} \neq -1 $

其次,获胜判定

先上一个结论:如果落子之前一方没有获胜且落子后获胜,则落子必然在获胜的连续五子中

考虑八联通搜索——枚举方向,步数

记录一个 $ cnt $ 数组,表示四个方向(八联通二合一)可拓展的最远距离

更新 $ cnt $ 后直接判断是否存在 $ j $ 使得 $ cnt_j \ge 5 $ 即可

注意事项:

  1. $ mp $ 初始化赋值 $ -1 $

  2. 方向数组八联通

  3. 数组大小需要充分(云落因此虚空调试 24h)

  4. $ cnt $ 数组初始化为 $ 1 $(想想为什么捏?)

  5. 注意输出的文末“!”

代码

没有删除调试代码,但似乎有辅助理解的作用?

#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;
}

后记

云落下班!

完结撒花

posted @ 2024-11-20 22:48  sunxuhetai  阅读(20)  评论(0)    收藏  举报