2257. 统计网格图中没有被保卫的格子数、检索标记

这篇博客探讨了如何通过优化算法来减少在处理二维数组时的重复计算,特别是在填充护卫保卫区域的过程中。文章提出了两种版本的解决方案:暴力版本和优化版本。暴力版本简单地遍历每个方向直到遇到障碍,而优化版本则考虑了如果遇到另一个护卫或已保卫区域的情况,提前结束当前方向的遍历,从而提高了效率。
摘要由CSDN通过智能技术生成

暴力版本:

直接使用一个二维数组保存、墙、护卫的信息。在保存护卫的信息时,处理这个护卫东西南北个个方向的方格,最后计算未被保卫的方格数。

class Solution {
public:
    int countUnguarded(int m, int n, vector<vector<int>>& guards, vector<vector<int>>& walls) {
        /*
        优化部分:
        // 记录已经被护卫访问的行和列
        // 行
        int visitedM[m];
        // 列
        int visitedN[n];
        */

        // 二维方格
        int cell[m][n];

        // 初始化方格
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                // 方格默认值
                cell[i][j] = 0;   
            }
        }        
  
        /*
        优化部分:  
        // 行未访问的标示值
        for(int i = 0; i < m; i++){
            visitedM[i] = 0;
        }

        // 列未访问的标识值
        for(int j = 0; j < n; j++){
            visitedN[j] = 0;
        }
        */

        // 填充墙的信息
        for(int i = 0; i < walls.size(); i++){
            // 墙的标示值
            cell[walls[i][0]][walls[i][1]] = 1;
        }
        
        // 填充护卫的信息,并且填充被保卫地区的信息
        for(int k = 0; k < guards.size(); k++){
            int i = guards[k][0], j = guards[k][1];
            
            // 护卫的标示值
            cell[i][j] = 2;
            
            /*
            优化部分:  
            // 行、列被访问的标识值
            visitedM[i] = 1;
            visitedN[j] = 1;
            */

            // 填充被保卫地区的信息
            // 处理护卫的北边
            int ii = i - 1;
            while(ii >= 0){
                /* 如果当前访问的位置:
                        1.是墙的位置,则停止访问
                */
                if(cell[ii][j] == 1){
                    break;
                }
                
                // 标记被保卫的地区
                cell[ii--][j] = 3;
            }

            // 处理护卫的南边
            ii = i + 1;
            while(ii < m){
                if(cell[ii][j] == 1){
                    break;
                }
                
                cell[ii++][j] = 3;
            }

            // 处理护卫的西边
            int jj = j - 1;
            while(jj >= 0){
                if(cell[i][jj] == 1){
                    break;
                }
                
                cell[i][jj--] = 3;
            }

            // 处理护卫的东边
            jj = j + 1;
            while(jj < n){
                if(cell[i][jj] == 1){
                    break;
                }
                
                cell[i][jj++] = 3;
            }
        }
        
        // 计算未被保卫的方格个数
        int ans = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(cell[i][j] == 0){
                    ans++;
                }
            }
        } 
        
        return ans;
    }
};

优化版本: 

较前一个版本,这里我们主要将优化点放在 “护卫东西南北个个方向的方格” 这段程序。分析我们可以知道,这段程序会有许多重复的工作。

分析如下:

例如当一个护卫在其四个方向中,如果某个方向遇到另一个护卫,其实这个时候当前的护卫可以停止对这个方向的访问;

例如当一个护卫在其四个方向中,如果某个方向遇到已经被保卫的位置,且当前访问方向(某一行或列)已经被另一个护卫访问,且该位置垂直方向(某一列或行)没有被另一个护卫访问,其实这个时候当前的护卫也可以停止对这个方向的访问;

// 优化版本
class Solution {
public:
    int countUnguarded(int m, int n, vector<vector<int>>& guards, vector<vector<int>>& walls) {
        // 记录已经被护卫访问的行和列
        // 行
        int visitedM[m];
        // 列
        int visitedN[n];
        
        // 二维方格
        int cell[m][n];

        // 初始化方格
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                // 方格默认值
                cell[i][j] = 0;   
            }
        }        

        // 行未访问的标示值
        for(int i = 0; i < m; i++){
            visitedM[i] = 0;
        }

        // 列未访问的标识值
        for(int j = 0; j < n; j++){
            visitedN[j] = 0;
        }

        // 填充墙的信息
        for(int i = 0; i < walls.size(); i++){
            // 墙的标示值
            cell[walls[i][0]][walls[i][1]] = 1;
        }
        
        // 填充护卫的信息,并且填充被保卫地区的信息
        for(int k = 0; k < guards.size(); k++){
            int i = guards[k][0], j = guards[k][1];
            
            // 护卫的标示值
            cell[i][j] = 2;
            
            // 行、列被访问的标识值
            visitedM[i] = 1;
            visitedN[j] = 1;

            // 填充被保卫地区的信息
            // 处理护卫的北边
            int ii = i - 1;
            while(ii >= 0){
                /* 如果当前访问的位置:
                        1.是另一个护卫的位置,则停止访问
                        2.是墙的位置,则停止访问
                        3.即非护卫也非墙的位置,仅仅是已经被保卫的位置,如果当前位置的行没有被访问 且 当前位置的列被访问,则停止访问
                */
                if(cell[ii][j] == 2 || cell[ii][j] == 1 || (cell[ii][j] == 3 && visitedM[ii] == 0 && visitedN[j] == 1)){
                    break;
                }
                
                // 标记被保卫的地区
                cell[ii--][j] = 3;
            }

            // 处理护卫的南边
            ii = i + 1;
            while(ii < m){
                if(cell[ii][j] == 2 || cell[ii][j] == 1 || (cell[ii][j] == 3 && visitedM[ii] == 0 && visitedN[j] == 1)){
                    break;
                }
                
                cell[ii++][j] = 3;
            }

            // 处理护卫的西边
            int jj = j - 1;
            while(jj >= 0){
                if(cell[i][jj] == 2 || cell[i][jj] == 1 || (cell[i][jj] == 3 && visitedM[i] == 1 && visitedN[jj] == 0)){
                    break;
                }
                
                cell[i][jj--] = 3;
            }

            // 处理护卫的东边
            jj = j + 1;
            while(jj < n){
                if(cell[i][jj] == 2 || cell[i][jj] == 1 || (cell[i][jj] == 3 && visitedM[i] == 1 && visitedN[jj] == 0)){
                    break;
                }
                
                cell[i][jj++] = 3;
            }
        }
        
        // 计算未被保卫的方格个数
        int ans = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(cell[i][j] == 0){
                    ans++;
                }
            }
        } 
        
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值