力扣 1001. 网格照明 (Python/C++)

题目

题目链接:1001.网格照明
在这里插入图片描述
在这里插入图片描述

题意分析

grid

  • grid是一个灯泡矩阵,矩阵的大小是 n x n
    在这里插入图片描述

lamps

  • lamps数组会给到一些点亮的灯泡坐标
  • 每一个点亮的灯泡会照亮(上下左右,对角线,反对角线)上的灯泡。

queries

  • queries数组中有一些坐标,希望知道该坐标的灯泡是否是亮的(无论是点亮还是照亮都算亮)
    查询操作:如果查询到的坐标的灯泡是亮的 返回1,否则返回 0

  • 查询坐标附近的8个格子(如果在grid中)的灯泡熄灭,被熄灭的灯泡一定是点亮
    该点被熄灭并不意味着它一定是暗的,因为可能被其他的灯泡照亮
    在这里插入图片描述
    如果蓝色和绿色的格子中有点亮的灯泡,就要熄灭它。

思路

先从最容易想到的方法开始:

  • 首先明确坐标:
    在这里插入图片描述

暴力解法

我们在点亮灯泡的时候,将它8个反向的灯泡全部照亮。
体现到代码就是将 grid矩阵赋值(0:暗;1:该点被照亮;2:该点是灯泡)

/* 
我希望这函数能够进行点亮和熄灭操作(它们本质上是一样的)
isBlub:表示这次操作是不是点亮操作
row,col:是操作的坐标
*/

void controlLights(vector<vector<int>>& grid, int row, int col, bool isBlub){
    int n = grid.size();
    // 8 个反向上x,y的改变量
    int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
    int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
    int state = isBlub?1:0; // 如果是点亮操作,接下来grid[x][y] 就会被赋值为1
    grid[row][col] = isBlub?2:0; // 如果是点亮操作,赋值为2
    for(int i=1;i<n;++i) {
        for(int j=0;j<8;++j){
            int x = row+i*dx[j];
            int y = col+i*dy[j];
            if(x>=0 && y>=0 && x<n && y<n &&grid[x][y]!=2)grid[x][y] = state;
        }
    }
}

接下来就是熄灭操作了
我们可以找出查询点周围的所有点亮的灯泡放到lights数组中,并将他们熄灭

  • 我采用的熄灭方法是,将lights中所有的灯泡都执行熄灭操作,但这有可能会熄灭别的点亮的灯泡照亮的灯泡
    在这里插入图片描述
  • 为了解决这个问题,我用set来存储所有的点亮的灯泡的坐标,并在每一次熄灭后,将剩余的灯泡重新进行点亮操作(使某些不应该熄灭的灯泡被照亮),这个set被命名为 blubs(灯泡)
/*
这里row,col是查询的坐标
我希望通过这个函数将范围内的灯泡熄灭

所有点亮的灯泡的坐标
set<pair<int,int>> blubs;
*/
 void turnOffLights(vector<vector<int>>& grid, int row, int col){
     int n = grid.size();
     vector<pair<int,int>> lights; // 周围存在的灯泡
     if(grid[row][col]==2) lights.push_back({row,col});
     for(int j=0;j<8;++j){
         int x = row+dx[j];
         int y = col+dy[j];
         if(x>=0 && y>=0 && x<n && y<n && grid[x][y]==2) { 
         	// 我们约定过 grid[x][y]=2时代表该点是点亮的灯泡
             lights.push_back({x,y});
         }
     }
     for(auto light : lights){
     	//灯泡要被熄灭了,从blubs中删掉它
         blubs.erase(blubs.find(light));
         controlLights(grid, light.first, light.second, false);
     }
 }

代码

很遗憾这种暴力的方法是超时的,但仍有学习意义(至少思路没有错),这里给出代码。
在这里插入图片描述

class Solution {
public:
    int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
    int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
    set<pair<int,int>> blubs;
    void controlLights(vector<vector<int>>& grid, int row, int col, bool isBlub){
        int n = grid.size();
        int state = isBlub?1:0;
        grid[row][col] = isBlub?2:0;
        for(int i=1;i<n;++i) {
            for(int j=0;j<8;++j){
                int x = row+i*dx[j];
                int y = col+i*dy[j];
                if(x>=0 && y>=0 && x<n && y<n &&grid[x][y]!=2)grid[x][y] = state;
            }
        }
    }
    void turnOffLights(vector<vector<int>>& grid, int row, int col){
        int n = grid.size();
        vector<pair<int,int>> lights;
        if(grid[row][col]==2) lights.push_back({row,col});
        for(int j=0;j<8;++j){
            int x = row+dx[j];
            int y = col+dy[j];
            if(x>=0 && y>=0 && x<n && y<n && grid[x][y]==2) {
                lights.push_back({x,y});
            }
        }
        for(auto light : lights){
            blubs.erase(blubs.find(light));
            controlLights(grid, light.first, light.second, false);
        }
    }

    vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
        vector<vector<int>> grid(n,vector<int>(n,0));
        vector<int> ans;
        for(auto coord : lamps){
            controlLights(grid, coord[0], coord[1],true);
            // lamps中的都是点亮的灯泡,加入blubs中
            blubs.insert({coord[0], coord[1]});
        }
        for(auto coord : queries){
            int row = coord[0];
            int col = coord[1];
            ans.push_back(grid[row][col]!=0);
            turnOffLights(grid, row, col);
            //每次熄灭灯泡后再点亮
            for(auto i : blubs){
                controlLights(grid, i.first, i.second, true);
            }
        }
        return ans;
    } 
};

哈希表

为什么暴力的方法会失败呢?

  • 我们知道,一个点亮的灯泡会照亮它8个反向的灯泡,这8个反向其实都是直线,没必要每一个坐标都存下来(这给遍历带来的时间的浪费,同时也有空间的浪费)。
    再解释一下,因为所有的点亮和熄灭操作都是在直线上,也就是每次熄灭一排或者点亮一排,因此任何一个点都与它直线上的点是相同地位的。
  • 我们使用哈希表加快查询的效率

如何将一行/列 的坐标简化成一行,并且使用上哈希表呢?

  • 将第 i 行表示成 r[i] (unordered_map<int,int> r
    这样r[i] 的值就i可以表示为这一行亮着的灯泡数
    同时也能避免熄灭导致的问题(熄灭某个灯泡导致熄灭其他灯泡照亮的灯泡)
    当我们点亮第 i 行灯泡时,直接 r[i] += 1,熄灭灯泡时 r[i] -= 1

接下来要解决对角线的表示

  • 我们要找出的是每个灯泡对角线不同的特征,同时每条对角线上灯泡对角线的特征是一样的
    在这里插入图片描述
    观察到反对角线上坐标的特点:x+y 在一条对角线上是一个恒定的数值
    (2,0) (0,2) => 2+0 = 1+1 <=(1,1))
    同理正对角线上的坐标特点是: x-y 在一条对角线上是一个恒定的数值
    因此我们可以把对角线上的灯泡表示为 dia1[x-y]dia2[x+y] (dia是diagonal对角线的缩写)
    当点亮第 i 个灯泡(坐标为(x,y))时,dia[x-y] += 1 dia2[x+y] += 1
    熄灭第 i 个灯泡时 dia[x-y] -= 1 dia2[x+y] -= 1

如何表示某个灯泡的明暗呢?

  • 当某一行会被两个灯泡照亮时,r[i] = 2 (结果了两次 r[i] += 1)
    那么只有两个灯泡都熄灭时,r[i] = 0,也就是结果两次 r[i] -= 1
    因此,该点的8个方向上的值都为0时,说明这个灯泡是暗的
    8个反向上只要有一个不为0,说明这个灯泡是亮的。

代码(哈希表)

一些小细节在代码注释中。

C++

在这里插入图片描述

class Solution {
public:
    vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
    // 总灯泡,指的是所有的点亮的灯泡(不是照亮的)
        set<pair<int,int>> lamp;
        unordered_map<int,int> r;
        unordered_map<int,int> c;
        unordered_map<int,int> dia1;
        unordered_map<int,int> dia2;
        vector<int> ans;
        for(auto item: lamps){
            int x = item[0];
            int y = item[1];
            pair<int,int> xy = {x,y};
            /*
            这个if判断绝不能省去,因为题目说可能会有重复的灯泡的坐标。
            那么同一盏灯不应该照亮周围的灯泡两次
            */
            if(lamp.find(xy)==lamp.end()){
                lamp.insert(xy);
                r[x] += 1;
                c[y] += 1;
                dia1[x-y] += 1;
                dia2[x+y] += 1;
            }
        }
        for(auto query : queries){
            int row = query[0];
            int col = query[1];
            // 只要有一个反向上被照亮过,就说明这个灯泡是亮的
            r[row] || c[col] || dia1[row-col] || dia2[row+col] ? ans.push_back(1) : ans.push_back(0);
            for(int x = row-1; x<row+2; ++x){
                for(int y=col-1; y<col+2; ++y){
                	// 如果(x,y) 是总灯泡中的某一个,就要消除它照亮的灯泡
                    if(x<0 || y<0 || x>n || y>n || lamp.find({x,y})==lamp.end()) continue;
                    // 这个灯泡已经被删除了,从总灯泡中移除
                    lamp.erase(lamp.find({x,y}));
                    r[x] -= 1;
                    c[y] -= 1;
                    dia1[x-y] -= 1;
                    dia2[x+y] -= 1;
                }
            }
        }
        return ans;
    }
};

Python

在这里插入图片描述

from collections import defaultdict
class Solution(object):
    def gridIllumination(self, n, lamps, queries):
        """
        :type n: int
        :type lamps: List[List[int]]
        :type queries: List[List[int]]
        :rtype: List[int]
        """
        lamp = set()
        r, c, dia1, dia2, ans = defaultdict(int), defaultdict(int) ,defaultdict(int), defaultdict(int), list()
        for x,y in lamps:
            if (x,y) in lamp:
                continue
            lamp.add((x,y))
            r[x] += 1
            c[y] += 1
            dia1[x-y] += 1
            dia2[x+y] += 1
            
        for i,j in queries:
            if r[i] or c[j] or dia1[i-j] or dia2[i+j]:
                ans.append(1)
            else:
                ans.append(0)
            for x in range(i-1, i+2):
                for y in range(j-1, j+2):
                    if x<0 or y<0 or x>=n or y>=n or (x,y) not in lamp:
                        continue
                    lamp.remove((x,y))
                    r[x] -= 1
                    c[y] -= 1
                    dia1[x-y] -= 1
                    dia2[x+y] -= 1
        return ans

Python3

在这里插入图片描述

from collections import defaultdict
class Solution:
    def gridIllumination(self, N: int, lamps: List[List[int]], queries: List[List[int]]) -> List[int]:
        r, c, dia1, dia2 = defaultdict(int), defaultdict(int) ,defaultdict(int), defaultdict(int)
        lamp = set()
        for x, y in lamps:
            if (x,y) not in lamp:
                r[x] += 1
                c[y] += 1
                dia1[x+y] += 1
                dia2[x-y] += 1
                lamp.add((x, y))
            
        res = list()
        for i, j in queries:
            if r[i] or c[j] or dia1[i + j] or dia2[i - j]:
                res.append(1)
            else:
                res.append(0)
            for x in [-1, 0, 1]:
                for y in [-1, 0, 1]:
                    nx = x + i
                    ny = y + j
                    if (nx, ny) in lamp:
                        r[nx] -= 1
                        c[ny] -= 1
                        dia1[nx+ny] -= 1
                        dia2[nx-ny] -= 1
                        lamp.remove((nx, ny))

        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值