题目链接:http://poj.org/problem?id=1970
题意:
有一个19 × 19 的五子棋棋盘,其中“0”代表未放入棋子,“1”代表黑色棋子,”2“代表白色棋子,如果某方的棋子在横,纵,斜这四个方向的连子数(连着的棋子数)恰好为5,那么此方就可以获胜。给你某一刻棋盘上的棋子状态,问你在此刻是哪一方获胜,亦或是平局,平局时仅输出“0”;如果是黑色方获胜,那么输出“1“,白色方获胜输出“2”,并且在第二行输出五连子最左边的棋子的坐标,如果在纵方向五连子,那么输出最上面的那个棋子的坐标。
思路:
首先,对于一个棋子来说,它可以和八个方向的棋子连在一起形成连子,但是这里并用不着对棋子的八个方向进行搜索,因为八个方向实质上可以认为是四个方向,即只需对某个棋子的四个方向进行搜索就OK 了,方向的选择也会影响到程序的复杂性,这里的做法是选择一个当前棋子作为起点(0,0),对这个棋子的”“右上”(-1,1),“右”(0,1),“右下”(1,1),“下”(1,0)这四个方向进行搜索,原因是如果这四个方向能找到五连子,那么作为起点的棋子的坐标即是题目要求的最左边的棋子的坐标,可以降低程序复杂性。方向确定了,搜索的状态就确定了,对于每个棋子有四个状态,所以又需要一个辅助数组来标记棋子的某个状态是否已被搜索过,用一个三维数组visit[i][j][k]代表坐标为(i, j)的棋子其k方向是否已被搜索过,k取值为1~4。然后从左到右、从上到下遍历图,分别从四个方向搜索统计就好了。
由于“右上”方向在搜索中的特殊性,所以这里要解释一些东西。在往“右上”方向搜索的过程中,如果一个棋子的“右上”方向没被搜索过,那么应该尽可能的往这个方向搜索(即使这个方向的其他棋子之前已被搜索过了,这里仍然要进行搜索)。
比如:
0 0 0 0 1...
0 0 0 1 0...
0 0 1 0 0...
0 1 0 0 0...
1 0 0 0 0...
...
首先搜索到的肯定是第一行的”1“,可知连子数为 1个, 但此时并不满足; 再搜索第二行的“1”,此时第一行的1虽然已经搜索过了,但是还需要再搜索一遍,可得连子数为2个; 当搜索到第三行的"1"时,上面的两个"1"仍然要进行搜索统计,可得连子数为3个......底下的两个“1”也是同理,虽然这里可以用记忆化,但是由于搜索树的层数并不深,所以影响不大。
然后当“右上”方向的延伸结果为5时,这里还需要判断“右上”这个方向的反方向(即右下)有没有一样的棋子(否则会出现6连子,不符合题意),之所以要判断“右上”的反方向是否有多余的棋子,是因为位于“右下”的棋子搜索顺序在当前棋子之后,所以“右下”若有相同的棋子,有可能使得本来的5连子形成6连子,但是“右”的反方向“左”、“右下”的反方向“右上”、"下"的反方向“上”,这三个反方向上的棋子的搜索顺序都在当前棋子之前,不用考虑是否会出现这个现象。
代码:
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <stack> 7 #include <queue> 8 #include <algorithm> 9 #include <string> 10 11 typedef long long LL; 12 using namespace std; 13 const int MAXN = 19; 14 const int stepX[] = {0, -1, 0, 1, 1};//四个方向 15 const int stepY[] = {0, 1, 1, 1, 0}; 16 int visit[MAXN + 3][MAXN + 3][7];//visit[i][j][k]表示位于[i][j]位置的棋子的k方向是否已被搜索过 17 int map[MAXN + 3][MAXN + 3];//存图 18 int cnt; 19 20 void DFS(int x, int y, int id, int dire) {//x,y为坐标,id为1或者2,dire代表方位,即确定方位的进行搜索 21 visit[x][y][dire] = 1; 22 int nex = x + stepX[dire], ney = y + stepY[dire]; 23 if(map[nex][ney] == id){ 24 ++cnt;//统计连子数 25 DFS(nex, ney, id, dire); 26 } 27 } 28 29 int main() { 30 //freopen("input", "r", stdin); 31 int t; 32 scanf("%d", &t); 33 while(t--) { 34 memset(visit, 0, sizeof(visit)); 35 memset(map, 0, sizeof(map)); 36 cnt = 0; 37 for(int i = 1; i <= 19; i++) { 38 for(int j = 1; j <= 19; j++) { 39 scanf("%d", &map[i][j]); 40 } 41 } 42 int leftX = -1, leftY = -1;//五连子最左边棋子的坐标 43 int win = 0;//获胜的标志 44 for(int i = 1; i <= 19; i++) { 45 for(int j = 1; j <= 19; j++) { 46 if(!map[i][j]) continue; 47 for(int k = 1; k <= 4; k++) { 48 if(!visit[i][j][k]) { 49 cnt = 1; 50 DFS(i, j, map[i][j], k); 51 if(cnt == 5 && map[i - stepX[k]][j - stepY[k]] != map[i][j]) {//判断是否为五连子且考虑反方向是否会造成六连子 52 win = map[i][j], leftX = i, leftY = j; 53 } 54 } 55 } 56 } 57 } 58 printf("%d ", win); 59 if(win) printf("%d %d ", leftX, leftY); 60 } 61 return 0; 62 }