统计可以提取的工件

标签: 数组 哈希表 模拟

难度: Medium

存在一个 n x n 大小、下标从 0 开始的网格,网格中埋着一些工件。给你一个整数 n 和一个下标从 0 开始的二维整数数组 artifactsartifacts 描述了矩形工件的位置,其中 artifacts[i] = [r1i, c1i, r2i, c2i] 表示第 i 个工件在子网格中的填埋情况:

  • (r1i, c1i) 是第 i 个工件 左上 单元格的坐标,且
  • (r2i, c2i) 是第 i 个工件 右下 单元格的坐标。

你将会挖掘网格中的一些单元格,并清除其中的填埋物。如果单元格中埋着工件的一部分,那么该工件这一部分将会裸露出来。如果一个工件的所有部分都都裸露出来,你就可以提取该工件。

给你一个下标从 0 开始的二维整数数组 dig ,其中 dig[i] = [ri, ci] 表示你将会挖掘单元格 (ri, ci) ,返回你可以提取的工件数目。

生成的测试用例满足:

  • 不存在重叠的两个工件。
  • 每个工件最多只覆盖 4 个单元格。
  • dig 中的元素互不相同。

示例 1:

输入:n = 2, artifacts = [[0,0,0,0],[0,1,1,1]], dig = [[0,0],[0,1]]
输出:1
解释: 
不同颜色表示不同的工件。挖掘的单元格用 'D' 在网格中进行标记。
有 1 个工件可以提取,即红色工件。
蓝色工件在单元格 (1,1) 的部分尚未裸露出来,所以无法提取该工件。
因此,返回 1 。

示例 2:

输入:n = 2, artifacts = [[0,0,0,0],[0,1,1,1]], dig = [[0,0],[0,1],[1,1]]
输出:2
解释:红色工件和蓝色工件的所有部分都裸露出来(用 'D' 标记),都可以提取。因此,返回 2 。 

提示:

  • 1 <= n <= 1000
  • 1 <= artifacts.length, dig.length <= min(n2, 105)
  • artifacts[i].length == 4
  • dig[i].length == 2
  • 0 <= r1i, c1i, r2i, c2i, ri, ci <= n - 1
  • r1i <= r2i
  • c1i <= c2i
  • 不存在重叠的两个工件
  • 每个工件 最多 只覆盖 4 个单元格
  • dig 中的元素互不相同

Submission

运行时间: 156 ms

内存: 57.3 MB

class Solution:
    def digArtifacts(self, n: int, artifacts: List[List[int]], dig: List[List[int]]) -> int:
        vis=[[0]*n for _ in range(n)]
        for a,b in dig:
            vis[a][b]=1
        ans=0
        for a,b,c,d in artifacts:
            f=1
            for i in range(a,c+1):
                for j in range(b,d+1):
                    if vis[i][j]==0:
                        f=0
                        break
                if f==0:
                    break
            if f: ans+=1
        return ans

Explain

这个题解首先通过构建一个二维数组 `vis` 来记录所有被挖掘的单元格,其中 `vis[i][j]` 为 1 表示单元格 `(i, j)` 已经被挖掘。接着,对每一个工件,检查其对应的矩形区域内的所有单元格是否都已被挖掘(即检查这些单元格在 `vis` 中的值是否都为 1)。如果一个工件的所有部分都被挖掘了,那么这个工件可以被提取,计数器 `ans` 加一。最后,返回可以提取的工件数量。

时间复杂度: O(L + A*4)

空间复杂度: O(n^2)

class Solution:
    def digArtifacts(self, n: int, artifacts: List[List[int]], dig: List[List[int]]) -> int:
        # 创建一个 n x n 的二维数组来标记挖掘的位置
        vis = [[0]*n for _ in range(n)]
        # 标记所有被挖掘的单元格
        for a, b in dig:
            vis[a][b] = 1
        # 初始化可以提取的工件数量为 0
        ans = 0
        # 遍历所有工件
        for a, b, c, d in artifacts:
            f = 1  # 用于标记工件是否可以提取
            # 检查工件所覆盖的所有单元格是否都被挖掘了
            for i in range(a, c+1):
                for j in range(b, d+1):
                    if vis[i][j] == 0:
                        f = 0
                        break
                if f == 0:
                    break
            # 如果工件可以被提取,增加计数
            if f: ans += 1
        return ans

Explore

在题解中选择使用二维数组`vis`而不是哈希表的主要原因是空间和时间效率。二维数组允许直接通过索引访问元素,这在本题中特别适用,因为需要频繁地检查每个单元格是否被挖掘。使用二维数组可以以 O(1) 的时间复杂度直接访问任何位置,而不需要额外的哈希计算,这样可以减少运算时间。此外,由于`n x n`的网格大小是预先确定的,使用二维数组可以直观地表示每个单元格的状态,而使用哈希表可能会增加不必要的内存开销和查找成本。

在实现中,如果`dig`数组中存在重复的挖掘坐标,实际上不会对算法的最终结果产生影响。这是因为即使一个单元格被重复标记为已挖掘(即`vis[a][b]`被多次设置为1),它仍然只计算为一个挖掘过的单元格。因此,这种重复不会错误地增加已挖掘工件的数量。由于这种重复不影响最终结果,不需要在算法中特别处理这一情况。

如果工件之间允许存在重叠,当前的算法需要调整以正确统计可提取的工件数量。具体来说,可以实施以下策略:为每个工件分配一个独立的计数器,记录其被完全挖掘的单元格数量。在遍历`dig`数组时,不仅将挖掘的单元格在`vis`中标记为1,同时更新所有包含该单元格的工件的计数器。每个工件的计数器需要达到其所覆盖的单元格总数才能被认为是完全挖掘的。这种方法需要额外的数据结构来跟踪每个工件的挖掘进度,并可能需要更复杂的逻辑来管理工件之间的重叠。