zoukankan      html  css  js  c++  java
  • [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

    提交再试:

    顺利通过!

  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/shawnhue/p/leetcode_130.html
Copyright © 2011-2022 走看看