leetcode 827: Making A Large Island 深度优先搜索and二维数组分块技术 (C++)

题目描述:
In a 2D grid of 0s and 1s, we change at most one 0 to a 1.

After, what is the size of the largest island? (An island is a 4-directionally connected group of 1s).

Example 1:

Input: [[1, 0], [0, 1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.

Example 2:

Input: [[1, 1], [1, 0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 1.

Example 3:

Input: [[1, 1], [1, 1]]
Output: 4
Explanation: Can't change any 0 to 1, only one island with area = 1.

解法一:DFS-深度优先搜索(The Time Exceeded)

       这道题很容易想到的且很暴力的一种解法是DFS。我们对二维vector里的每一个‘0’处理(假设其坐标为(i,j))。首先将其变为‘1’,然后以其坐标为起点使用DFS算法,遍历其4-连接点并对每点调用递归获得该区块的大小,最后将其还原为0。我们需要维持一个当前所有区块面积的最大值。当遍历完所有的值为‘0’的点后,我们所维持的这个便是结果。此外,我们需要一个与输入grid同等大小的二维bool型vector来标志在当次DFS中,grid里的每个元素是否已经被访问过,每次DFS后将其用temp归零。
       具体的DFS算法命名为calculate,接收坐标点(i,j)及grid,visit。当i,j在grid范围内时返回计数+1并递归坐标的4-连接点。
       bool变量‘haszero’标志grid中是否有0,没有的话直接返回grid中1的个数。
       bool hasone函数是对计算量的一个优化,如过我们遍历所有0会加大计算量。我们先判断一个位置的0周围是否有1,有的话再对其调用DFS函数。
       但遗憾的是,这种C++方法被判定超时,因为每次对0进行遍历且进行DFS,最差情况的代码时间复杂度为O((MN)^2)。但是相同方法的JAVA代码却可以通过。
                                              

class Solution {
public:
    int largestIsland(vector<vector<int>>& grid){
    int m=grid.size();
    int n=grid[0].size();
    int result=0;
    bool haszero=0;
    if(grid==vector<vector<int>>(n,vector<int>(m,0))) return 1;
    vector<vector<bool>>visit(n,vector<bool>(m,false));
    vector<vector<bool>>temp(n,vector<bool>(m,false));
    for(int i=0;i<m;i++){
       for(int j=0;j<n;j++){
            if(grid[i][j]==0){
            if(hasone(i,j,grid)==true){
                 grid[i][j]=1;
                 result=max(result,calculate(i,j,grid,visit));
                 if(result==m*n) return result;
                 grid[i][j]=0;
                 visit=temp;
                 }
                 haszero=1;
                 }
          }
    }
    return haszero?result:m*n;
    }

   int calculate(int i,int j,vector<vector<int>> grid,vector<vector<bool>> &visit){
                  if(i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == 0||visit[i][j]) return 0;
                  visit[i][j]=true;
                  int curSum=1+calculate(i+1,j,grid,visit)+calculate(i-1,j,grid,visit)+calculate(i,j-1,grid,visit)+calculate(i,j+1,grid,visit);
                  return curSum;                  
                  }
   bool hasone(int i,int j,vector<vector<int>> grid){
              if(!(i < 0 || j < 0 || (i +1)>= grid.size() || j >= grid[0].size() || grid[i+1][j] == 0)) return true;
              if(!((i -1)< 0 || j < 0 || i>= grid.size() || j >= grid[0].size() || grid[i-1][j] == 0)) return true;
              if(!(i < 0 || j < 0 || (i)>= grid.size() || (j+1) >= grid[0].size() || grid[i][j+1] == 0)) return true;
              if(!(i < 0 || (j-1) < 0 || (i)>= grid.size() || j >= grid[0].size() || grid[i][j-1] == 0)) return true;
              return false;
             }                               
};

 


解法二:二维数组分块技术
          
         解法一之所以不通过,是因为每次遍历0的时候都需要对grid进行一次DFS,实际上我们会对一个‘1’块多次重复计算。那么如果我们先把每个块的大小计算(函数dfs),把块中的每个像素用相同的值(value,初值设为2以和原grid的0与1做区分,每次循环后+1)标定出来,同时,不同的块的标定值这不一样,它们是colourful的。这里我们要用到一个名为lands的unordered_map将不同颜色的块和该块的大小进行一一映射。后再遍历grid中的每个0位置上的点的4方向上的块的大小(函数connectLand,同时要注意不要加到有相同颜色的块)并取维持一个其的最大值即为结果。如下图(图片来自leetcode 827 Discuss里的votrubac):

 

 

                                         

 

       在DFS因为已经被visit的‘1’会被赋值为彩色,所以我们不需要像解法一那样创建一个visit数组。在connectLand函数里的返回值要+1以表示将我们要改变的‘0’坐标变为‘1’后增加的总面积。

        这样算法的时间复杂度就降低到了O(N*M),得以通过。

class Solution {   
public:
    int m=0;
    int n=0;
    unordered_map<int,int>lands;
    int largestIsland(vector<vector<int>>& grid){
    m=grid.size();
    n=grid[0].size();
    int value=2;
    int result=0;
    bool haszero=0;
    for(int i=0;i<m;i++){
       for(int j=0;j<n;j++){
          if(grid[i][j]==1){
             int curSum=0;
             dfs(i,j,value,grid,curSum);
             lands[value]=curSum;
             value++;
            }   
          }
       }
    for(int i=0;i<m;i++){
       for(int j=0;j<m;j++){
           if(grid[i][j]==0){
            haszero=1;
            result=max(result,connectLand(i,j,grid));
           }
          }
       }
    return haszero?result:m*n;
    }

    void dfs(int i,int j,int value,vector<vector<int>>& grid,int &curSum){
        if(i<0||i>=grid.size()||j<0||j>=grid[0].size()) return;
        if(grid[i][j]!=1) return;
        if(grid[i][j]==1){
        grid[i][j]=value;
        curSum++;
        }
        dfs(i+1,j,value,grid,curSum);
        dfs(i-1,j,value,grid,curSum);
        dfs(i,j+1,value,grid,curSum);
        dfs(i,j-1,value,grid,curSum);
        return;
    }

    int connectLand(int i,int j,vector<vector<int>> grid){
    int sum=0;
    int flag1=0,flag2=0,flag3=0;
    if((i+1)>=0&&(i+1)<grid.size()&&j>=0&&j<grid[0].size())
    {   
       if((grid[i+1][j]!=0)){
       sum=sum+lands[grid[i+1][j]];
       flag1=grid[i+1][j];
       }
    }
    if((i-1)>=0&&(i-1)<grid.size()&&j>=0&&j<grid[0].size())
    {
      if((grid[i-1][j]!=0)&&(grid[i-1][j]!=flag1)){
      sum=sum+lands[grid[i-1][j]];
      flag2=grid[i-1][j];
      }
    }
    if(i>=0&&i<grid.size()&&(j+1)>=0&&(j+1)<grid[0].size())
    {
      if((grid[i][j+1]!=0)&&(grid[i][j+1]!=flag1)&&(grid[i][j+1]!=flag2)){
      sum=sum+lands[grid[i][j+1]];
      flag3=grid[i][j+1];
      }
    }
    if(i>=0&&i<grid.size()&&(j-1)>=0&&(j-1)<grid[0].size())
    {
      if((grid[i][j-1]!=0)&&(grid[i][j-1]!=flag1)&&(grid[i][j-1]!=flag2)&&(grid[i][j-1]!=flag3))
      sum=sum+lands[grid[i][j-1]];
    }
    return sum+1;
    }
};

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值