给出一个包含'X'和'O'的2D板,捕获所有'X'围绕的区域。
一个区域被捕获是指翻转被围绕区域中的所有'O'为'X'。
例如,
X X X X
X O O X
X X O X
X O X X
在运行你的函数后,板子将变为:
X X X X
X X X X
X X X X
X O X X
初始思路
判断一个O点是否被X围绕,其实就是看从该点出发有没有一条路径能走到二维数组的区域外。在程序语言中,向上下左右移动分别为:[i-1, j], [i+1, j], [i, j-1], [i, j+1]。而找路径的步骤就是从O点出发向附近的O点不断移动的过程。要实现“不断移动”,一种方案是使用递归调用。由于极端情况下四个方向都能移动,在矩阵较大的时候可以想象递归调用栈的层数将会很多。而另一种比较直观的方案就是不断重复扫描二维数组,做出移动的动作,直到没有点可以移动为止。这种方案看起来比较直观,但是还需要解决几个问题:
1. 如果在移动过程中有走到边界的情况,即表示改点没有被围绕。要怎么要判断边界点?
可以通过i-1 < 0, i + 1 >= board.size(), j - 1 < 0, j + 1 >= board[0].size()这四个条件,只要某点坐标符合其中一个条件,它就是在边界上的点。
2. 由于四个方向移动,如何判断已走过的点?
由于我们只向O点移动,可以在移动到某点后将该点的值修改,如改为'P'。每次扫描都以P点作为移动的起点。最后根据是否找到路径的情况将P改为X或O。
3. 如何判断没有点可以移动?
很简单,我们可以在每次扫描前将一个标志设为false。在该次扫描中只要有过移动,将其置为true。扫描完后根据该变量值决定是否继续即可。
4. 如何找P点?
一次二重循环肯定是不够的,因为二重循环是不断地向右下移动。而P点可以向左或向上移动。这里我们先采用一个最简单的方案,不断地从最左上角即[0,0]开始找P,直到没有P点可以移动。这样一定可以保证不会有漏掉的P点。但可以看到将会有一个三重循环。
好了,看起来解决方案已经有了,让我们模拟一下:
遍历至[1,1],发现O点,将其置为P。开始扫描
[1,1]发现P [1,2]发现P [2,2]发现P 没有路径,将P替换成X
只能向右 只能向下 无路可走,没有路径,返回
X X X X X X X X X X X X X X X X X X X X
X P O X X P P X X P P X X P P X X X X X
X X O X X X O X X X P X X X P X X X X X
X O X X X O X X X O X X X O X X X O X X
遍历至[3,1],发现O点,将其置为P。开始扫描
[3, 1]发现P 有路径,将P替换回O
无路可走,有路径
X X X X X X X X X X X X
X X X X X X X X X X X X
X X X X X X X X X X X X
X P X X X P X X X O X X
看来的确能得出例子中的答案。好了,那么开始写代码吧:
1 class Solution 2 { 3 public: 4 void solve(std::vector<std::vector<char>> &board) 5 { 6 for(int i = 0; i < board.size(); ++i) 7 { 8 for(int j = 0; j < board[i].size(); ++j) 9 { 10 if(board[i][j] == 'O') 11 { 12 if(!FindPath(board, i, j)) 13 { 14 ReplaceBack(board, SURROUNDED); 15 } 16 else 17 { 18 ReplaceBack(board, NOT_SURROUNDED); 19 } 20 } 21 } 22 } 23 } 24 private: 25 enum ReplaceMethod 26 { 27 SURROUNDED, 28 NOT_SURROUNDED 29 }; 30 31 void ReplaceBack(std::vector<std::vector<char>> &board, ReplaceMethod replaceMethod) 32 { 33 for(int i = 0; i < board.size(); ++i) 34 { 35 for(int j = 0; j < board[i].size(); ++j) 36 { 37 if(board[i][j] == 'P') 38 { 39 if(replaceMethod == SURROUNDED) 40 { 41 board[i][j] = 'X'; 42 } 43 else 44 { 45 board[i][j] = 'O'; 46 } 47 } 48 } 49 } 50 } 51 52 bool FindPath(std::vector<std::vector<char>> &board, int X, int Y) 53 { 54 board[X][Y] = 'P'; 55 56 bool hasPath = false; 57 bool canMove = false; 58 59 while(true) 60 { 61 canMove = false; 62 for(int i = 0; i < board.size(); ++i) 63 { 64 for(int j = 0; j < board[i].size(); ++j) 65 { 66 if(board[i][j] == 'P') 67 { 68 if(i - 1 < 0) 69 { 70 hasPath = true; 71 } 72 else 73 { 74 if(board[i-1][j] == 'O') 75 { 76 board[i-1][j] = 'P'; 77 canMove = true; 78 79 } 80 } 81 82 if(j - 1 < 0) 83 { 84 hasPath = true; 85 } 86 else 87 { 88 if(board[i][j - 1] == 'O') 89 { 90 board[i][j - 1] = 'P'; 91 canMove = true; 92 93 94 } 95 } 96 97 if(i + 1 >= board.size()) 98 { 99 hasPath = true; 100 } 101 else 102 { 103 if(board[i+1][j] == 'O') 104 { 105 board[i+1][j] = 'P'; 106 canMove = true; 107 } 108 } 109 110 if(j + 1 >= board[0].size()) 111 { 112 hasPath = true; 113 } 114 else 115 { 116 if(board[i][j + 1] == 'O') 117 { 118 board[i][j + 1] = 'P'; 119 canMove = true; 120 } 121 } 122 } 123 } 124 } 125 if(!canMove) 126 { 127 return hasPath; 128 } 129 } 130 return hasPath; 131 } 132 133 };