一、题干: 网格照明

在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态。
给你一个由灯的位置组成的二维数组 lamps ,其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。
当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 、同一 和两条 对角线 上的 所有其他单元格
另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] 。对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的,则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] ,关闭 位于单元格 grid[rowj][colj] 上及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯。
返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果,1 表示照亮,0 表示未照亮。

示例 1:
image.png
输入:n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]] 输出:[1,0] 解释:最初所有灯都是关闭的。在执行查询之前,打开位于 [0, 0] 和 [4, 4] 的灯。第 0 次查询检查 grid[1][1] 是否被照亮(蓝色方框)。该单元格被照亮,所以 ans[0] = 1 。然后,关闭红色方框中的所有灯。
image.png
第 1 次查询检查 grid[1][0] 是否被照亮(蓝色方框)。该单元格没有被照亮,所以 ans[1] = 0 。然后,关闭红色矩形中的所有灯。
image.png
示例 2:
输入:n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,1]] 输出:[1,1]
示例 3:
输入:n = 5, lamps = [[0,0],[0,4]], queries = [[0,4],[0,1],[1,4]] 输出:[1,1,0]

提示:

  • 1 <= n <= 109
  • 0 <= lamps.length <= 20000
  • 0 <= queries.length <= 20000
  • lamps[i].length == 2
  • 0 <= rowi, coli < n
  • queries[j].length == 2
  • 0 <= rowj, colj < n

二、解题思路:

1、初版:

第一反应:暴力求解,但是如果将整个网格数组模拟出来,显然超内存,如下所示:

  1. func gridIllumination(n int, lamps [][]int, queries [][]int) []int {
  2. var arr [10e9][10e9]int//超内存
  3. var res []int
  4. var x1 int
  5. var y1 int
  6. //turn on,若是灯,则+1,若是被照亮,则+2
  7. for i:=0;i<len(lamps);i++ {
  8. x:=lamps[i][0]
  9. y:=lamps[i][1]
  10. if arr[x][y]&1==0{//偶数:no lamp
  11. arr[x][y]+=1
  12. for a:=0;a<n;a++{
  13. arr[x][a]+=2
  14. arr[a][y]+=2
  15. }
  16. for x1,y1=x-1,y-1;x1>=0&&y1>=0;x1,y1=x1-1,y1-1{
  17. arr[x1][y1]+=2
  18. }
  19. for x1,y1=x+1,y+1;x1<n&&y1<n;x1,y1=x1+1,y1+1{
  20. arr[x1][y1]+=2
  21. }
  22. for x1,y1=x+1,y-1;x1<n&&y1>=0;x1,y1=x1+1,y1-1{
  23. arr[x1][y1]+=2
  24. }
  25. for x1,y1=x-1,y+1;x1>=0&&y1<n;x1,y1=x1-1,y1+1{
  26. arr[x1][y1]+=2
  27. }
  28. }
  29. }
  30. //query
  31. for i:=0;i<len(queries);i++ {
  32. x:=queries[i][0]
  33. y:=queries[i][1]
  34. if arr[x][y]>0 {
  35. res=append(res,1)
  36. for x1,y1,ii:=x-1,y-1,1;ii<9;x1,y1,ii=x-1+ii/3,y-1+ii%3,ii+1{
  37. if x1<0||y1<0||x1>=n||y1>=n{
  38. continue
  39. }
  40. if arr[x1][y1]&1==1{//奇数
  41. //turn off
  42. arr[x1][y1]-=1
  43. for a:=0;a<n;a++{
  44. arr[x][a]+=2
  45. arr[a][y]+=2
  46. }
  47. for x1,y1=x-1,y-1;x1>=0&&y1>=0;x1,y1=x1-1,y1-1{
  48. arr[x1][y1]+=2
  49. }
  50. for x1,y1=x+1,y+1;x1<n&&y1<n;x1,y1=x1+1,y1+1{
  51. arr[x1][y1]+=2
  52. }
  53. for x1,y1=x+1,y-1;x1<n&&y1>=0;x1,y1=x1+1,y1-1{
  54. arr[x1][y1]+=2
  55. }
  56. for x1,y1=x-1,y+1;x1>=0&&y1<n;x1,y1=x1-1,y1+1{
  57. arr[x1][y1]+=2
  58. }
  59. }
  60. }
  61. }else{
  62. res=append(res,0)
  63. }
  64. }
  65. return res
  66. }

2、改进:

实际上,只需要记录对应的行,列和正反对角线上的灯数即可。

  1. 需求1:满足queries查找,
    解决方案:使用四个map分别记录四个不同方向上的灯数,验证queries[j]是否被四个方向中的任意一个方向照亮。
  2. 需求2:实现关闭九宫格内的灯,

解决方案:使用一个map记录所有坐标上的灯,如何通过坐标(x,y)唯一标识每个灯?使用n*x+y作为key来唯一标识每个灯。

因为0<=x,y<n,且x,y都是整数,n*x+y的值没有重复的,可证如下: 假设在范围内存在两个不同的点(x1,y1)和(x2,y2),使得Day02:网格照明(困难) - 图4相等, 则:Day02:网格照明(困难) - 图5,即Day02:网格照明(困难) - 图6, 但是因为Day02:网格照明(困难) - 图7都是[0,n-1]范围内的整数,所以Day02:网格照明(困难) - 图8的范围都是[-(n-1),(n-1)],故Day02:网格照明(困难) - 图9不可能等于n, 即Day02:网格照明(困难) - 图10不成立, 综上所述,得证。

  1. const(
  2. ON,OFF=1,-1
  3. )
  4. func gridIllumination(n int, lamps [][]int, queries [][]int) []int {
  5. var row=make(map[int]int)
  6. var col=make(map[int]int)
  7. var diagonal=make(map[int]int)
  8. var antidiagonal=make(map[int]int)
  9. var isLamp=make(map[int]bool)
  10. turn:=func(x,y,flag int){
  11. row[x]+=flag
  12. col[y]+=flag
  13. diagonal[y-x]+=flag
  14. antidiagonal[y+x]+=flag
  15. }
  16. for _,lamp:=range lamps{
  17. x,y:=lamp[0],lamp[1]
  18. if !isLamp[x*n+y]{
  19. isLamp[x*n+y]=true
  20. turn(x,y,ON)
  21. }
  22. }
  23. var res =make([]int,len(queries))
  24. for i,q:=range queries{
  25. x,y :=q[0],q[1]
  26. if row[x]>0||col[y]>0||diagonal[y-x]>0||antidiagonal[y+x]>0{
  27. res[i]=1
  28. }
  29. //遍历九宫格
  30. for x1,y1,j:=x-1,y-1,1;j<9;x1,y1,j=x-1+j/3,y-1+j%3,j+1{
  31. if x1<0||y1<0||x1>=n||y1>=n{
  32. continue
  33. }
  34. if isLamp[x1*n+y1]{
  35. isLamp[x1*n+y1]=false
  36. turn(x1,y1,OFF)
  37. }
  38. }
  39. }
  40. return res
  41. }