[LeetCode 130] - 围绕区域(Surrounded Regions)

问题

给出一个包含'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 };
View Code

提交后Judge Small顺利通过。然后…… Judge Large果然失败了,处理一个大概250*250的输入时超时。

 

优化

存在这么多n重循环,失败还是意料之中的。分析代码可以发现,循环最多的地方就在发现O点之后调用的FindPath函数里,对这个函数的调用和函数自己的实现肯定是优化的地方。

先来看对它的调用。每次FindPath返回后,如果找到了路径,我们会将所有P点替换回O点。然而后面再发现这些已经被验证有路径的O点时,又会进入FindPath做一遍查找。为了避免这些不必要的查找,可以改为不将P点换回O点,而是换为另一个标志,如'D'。这样做以后,在最后要多做一次对二维数组的遍历来将D点替换回O点。但是在有路径而且路径很长时这1次遍历相比至少重复O点个数次遍历还是划算的。

再来看FindPath的实现。为了实现简便,我们会不断的对二维数组从头开始遍历直到没有P点可以移动。由于这是一个三重循环,效率可想而知。前面提到过因为遍历二维数组下标是不断增加的,所以遍历只向右下移动。为了处理P点向左或向上移动的情况我们才采取了这种方案。那我们是不是可以通过修改下标的方式来实现:

如果向上移动,我们期望从本次横坐标-1的横坐标开始继续遍历。由于对横坐标的遍历在外层循环中,我们还需要终止内层循环:

1 if(board[i-1][j] == 'O')
2 {
3     board[i-1][j] = 'P';
4 
5         //注意因为for循环本身会对i进行递增操作,需要减2才能达到
6         //下次循环i从当前i-1开始的效果                            
7     i -= 2;
8         break;
9 }
View Code

注意因为for循环本身会对i进行递增操作,需要减2才能达到下次循环i从当前i-1开始的效果。

如果向左移动,我们期望从本次纵坐标-1的纵坐标开始继续遍历。由于对纵坐标的遍历就在当前内层循环中,直接重新执行循环即可:

1 if(board[i][j - 1] == 'O')
2 {
3     board[i][j - 1] = 'P';
4                             
5     j -= 2;
6         continue;
7 }
View Code

好了,经过这两个改动,循环的次数应该大大减少了。把所有改动集成进来:

  1 class Solution
  2 {
  3 public:
  4     void solve(std::vector<std::vector<char>> &board)
  5     {
  6         
  7         for(int i = 0; i < board.size(); ++i)
  8         {
  9             for(int j = 0; j < board[i].size(); ++j)
 10             {
 11                 if(board[i][j] == 'O')
 12                 {
 13                     if(!FindPath(board, i, j))
 14                     {
 15                         ReplaceBack(board, SURROUNDED);
 16                     }
 17                     else
 18                     {
 19                         ReplaceBack(board, NOT_SURROUNDED);
 20                     }
 21                 }
 22             }
 23         }
 24         
 25         
 26     for(int i = 0; i < board.size(); ++i)
 27         {
 28             for(int j = 0; j < board[i].size(); ++j)
 29             {
 30                 if(board[i][j] == 'D')
 31                 {
 32                     board[i][j] = 'O';
 33                 }
 34             }
 35         }
 36         
 37         
 38     }
 39 private:
 40     enum ReplaceMethod
 41     {
 42         SURROUNDED,
 43         NOT_SURROUNDED
 44     };
 45 
 46     void ReplaceBack(std::vector<std::vector<char>> &board, ReplaceMethod replaceMethod)
 47     {
 48         for(int i = 0; i < board.size(); ++i)
 49         {
 50             for(int j = 0; j < board[i].size(); ++j)
 51             {
 52                 if(board[i][j] == 'P')
 53                 {
 54                     if(replaceMethod == SURROUNDED)
 55                     {
 56                         board[i][j] = 'X';
 57                     }
 58                     else
 59                     {
 60                         board[i][j] = 'D';
 61                     }
 62                 }
 63             }
 64         }
 65     }
 66 
 67     bool FindPath(std::vector<std::vector<char>> &board, int X, int Y)
 68     {
 69                 board[X][Y] = 'P';
 70         
 71         bool hasPath = false;
 72 
 73         for(int i = 0; i < board.size(); ++i)
 74         {
 75             for(int j = 0; j < board[i].size(); ++j)
 76             {
 77                 if(board[i][j] == 'P')
 78                 {
 79                     if(i - 1 < 0)
 80                     {
 81                         hasPath = true;
 82                     }
 83                     else
 84                     {
 85                         if(board[i-1][j] == 'O')
 86                         {
 87                             board[i-1][j] = 'P';
 88                             
 89                             i -= 2;
 90                             break;
 91                         }
 92                     }
 93                     
 94                     if(j - 1 < 0)
 95                     {
 96                         hasPath = true;
 97                     }
 98                     else
 99                     {
100                         if(board[i][j - 1] == 'O')
101                         {
102                             board[i][j - 1] = 'P';
103                             
104                             j -= 2;
105                             continue;
106                         }
107                     }
108                     
109                     if(i + 1 >= board.size())
110                     {
111                         hasPath = true;
112                     }
113                     else
114                     {
115                         if(board[i+1][j] == 'O')
116                         {
117                             board[i+1][j] = 'P';                            
118                         }
119                     }
120                     
121                     if(j + 1 >= board[0].size())
122                     {
123                         hasPath = true;
124                     }
125                     else
126                     {
127                         if(board[i][j + 1] == 'O')
128                         {
129                             board[i][j + 1] = 'P';        
130                         }
131                     }    
132                 }    
133             }    
134         }
135         return hasPath;
136     }
137 };
View Code

提交再试:

顺利通过!

posted @ 2013-05-22 23:32  Shawnone  阅读(2643)  评论(0编辑  收藏  举报