UVa 260 Il Gioco dell‘X
题目分析
“Il Gioco dell’X\texttt{Il Gioco dell'X}Il Gioco dell’X” 是一种在菱形棋盘上进行的双人游戏,黑方和白方轮流放置棋子,目标分别是将上下边界或左右边界用己方棋子连通。题目保证棋盘被完全填满且必定有一方获胜,要求我们根据给定的完整棋盘判断胜者。
棋盘虽然用N×NN \times NN×N的矩阵表示,但邻接规则与普通四连通或八连通不同。题目定义的邻接关系为:
对于位置(i,j)(i, j)(i,j),其邻居包括:
(i−1,j−1), (i−1,j), (i,j−1), (i,j+1), (i+1,j), (i+1,j+1) (i-1, j-1),\ (i-1, j),\ (i, j-1),\ (i, j+1),\ (i+1, j),\ (i+1, j+1)(i−1,j−1),(i−1,j),(i,j−1),(i,j+1),(i+1,j),(i+1,j+1)
前提是这些坐标不超出[1,N][1, N][1,N]的范围。
这个邻接关系实际上描述了一个六边形网格的连通性。如果将棋盘的行和列按一定方式映射,可以发现每个格子与周围六个格子相邻(边界上的格子邻居较少)。
胜负判定规则
- 黑方(
Black)的目标:用'b'棋子连接第111行和第NNN行(上下边界)。 - 白方(
White)的目标:用'w'棋子连接第111列和第NNN列(左右边界)。
题目说明游戏不可能以平局结束,因此每局必有一方获胜。
解题思路
核心思想
判断某一方是否获胜,本质上是判断在给定的网格上,是否存在一条由该方棋子组成的路径,连接其目标的两条边界。
由于棋盘规模最大为200×200200 \times 200200×200,可以使用深度优先搜索(DFS\texttt{DFS}DFS)或广度优先搜索(BFS\texttt{BFS}BFS)来执行连通性检测。
黑方判定
对于黑方,需要检查是否存在一条从第000行(实际为第一行,代码中索引从000开始)到第n−1n-1n−1行(最后一行)的'b'路径。
具体步骤:
- 遍历第000行中的所有列。
- 如果某个位置(0,i)(0, i)(0,i)是
'b',则从该点出发执行DFS\texttt{DFS}DFS,标记所有能到达的'b'格子。 - 在DFS\texttt{DFS}DFS结束后,检查最后一行(第n−1n-1n−1行)是否有被标记的格子。
- 如果存在,则黑方获胜。
白方判定
本题提供的代码实际上只检查了黑方是否获胜。如果黑方没有获胜,根据题目保证必有胜者的前提,则白方获胜。这是因为:
- 棋盘完全填满,无空位。
- 游戏不可能平局。
- 黑白双方的目标边界是正交的,不可能同时连通(否则会形成交叉,与邻接规则矛盾)。
因此,只需要检查黑方是否连通上下边界即可。若黑方未胜,输出"W"。
邻接方向的处理
题目定义的六个邻居方向与常规的二维数组索引需要对应好。在代码中,方向数组定义为:
intstep[6][2]={{0,-1},{-1,-1},{-1,0},{0,1},{1,1},{1,0}};这里(x,y)(x, y)(x,y)分别表示行号和列号(xxx为行,yyy为列)。
算法复杂度
- 对每个起点进行DFS\texttt{DFS}DFS的复杂度为O(N2)O(N^2)O(N2),因为每个格子最多被访问一次。
- 最坏情况下,第000行有NNN个起点,但一旦找到连通路径就提前终止,实际运行效率较高。
- 总时间复杂度O(N2)O(N^2)O(N2),空间复杂度O(N2)O(N^2)O(N2),对于N≤200N \leq 200N≤200完全可行。
代码实现
以下为通过代码,添加了详细的中文注释:
// Il Gioco dell'X// UVa ID: 260// Verdict: Accepted// Submission Date: 2016-05-10// UVa Run Time: 0.200s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;charboard[200][200];// 存储棋盘,'b' 表示黑棋,'w' 表示白棋boolvisited[200][200];// 标记 DFS 过程中已访问的格子intn;// 棋盘大小 N// 六边形网格的六个邻接方向,对应题目中定义的邻居关系intstep[6][2]={{0,-1},{-1,-1},{-1,0},{0,1},{1,1},{1,0}};// 深度优先搜索,从 (x, y) 出发,标记所有连通的 'b' 格子voiddfs(intx,inty){visited[x][y]=true;for(inti=0;i<6;i++){intxx=x+step[i][0],yy=y+step[i][1];// 检查边界、是否为黑棋、是否未访问if(xx>=0&&xx<n&&yy>=0&&yy<n&&board[xx][yy]=='b'&&!visited[xx][yy])dfs(xx,yy);}}intmain(){cin.tie(0);cin.sync_with_stdio(false);intcases=0;// 游戏局数计数器while(cin>>n,n){// 读入棋盘for(inti=0;i<n;i++)for(intj=0;j<n;j++)cin>>board[i][j];// 检查黑方是否获胜boolblackWin=false;// 遍历第一行(上边界)的所有格子for(inti=0;i<n;i++){// 如果上边界的某个格子是黑棋,则从它开始 DFSif(board[0][i]=='b'){// 重置访问标记数组for(intx=0;x<n;x++)for(inty=0;y<n;y++)visited[x][y]=false;dfs(0,i);// 从 (0, i) 开始搜索// 检查最后一行(下边界)是否有被标记的黑棋for(intk=0;k<n;k++)if(visited[n-1][k]){blackWin=true;break;}}// 如果已经判定黑方获胜,提前结束循环if(blackWin)break;}// 输出结果:局数编号和胜者(B 或 W)cases++;cout<<cases<<" "<<(blackWin?"B":"W")<<endl;}return0;}总结
本题的关键在于正确理解六边形网格的邻接关系,并利用DFS\texttt{DFS}DFS进行连通性检测。由于游戏保证必有胜者,我们只需判定黑方是否连通上下边界,即可得出最终结果。代码采用简洁的DFS\texttt{DFS}DFS实现,时间复杂度O(N2)O(N^2)O(N2),足以应对题目给定的最大规模N=200N = 200N=200。
