沙地治理

标签: 数组 数学

难度: Hard

在力扣城的沙漠分会场展示了一种沙柳树,这种沙柳树能够将沙地转化为坚实的绿地。 展示的区域为正三角形,这片区域可以拆分为若干个子区域,每个子区域都是边长为 `1` 的小三角形,其中第 `i` 行有 `2i - 1` 个小三角形。 初始情况下,区域中的所有位置都为沙地,你需要指定一些子区域种植沙柳树成为绿地,以达到转化整片区域为绿地的最终目的,规则如下: - 若两个子区域共用一条边,则视为相邻; >如下图所示,(1,1)和(2,2)相邻,(3,2)和(3,3)相邻;(2,2)和(3,3)不相邻,因为它们没有共用边。 - 若至少有两片绿地与同一片沙地相邻,则这片沙地也会转化为绿地 - 转化为绿地的区域会影响其相邻的沙地 ![image.png](https://pic.leetcode-cn.com/1662692397-VlvErS-image.png) 现要将一片边长为 `size` 的沙地全部转化为绿地,请找到任意一种初始指定 **最少** 数量子区域种植沙柳的方案,并返回所有初始种植沙柳树的绿地坐标。 **示例 1:** >输入:`size = 3` >输出:`[[1,1],[2,1],[2,3],[3,1],[3,5]]` >解释:如下图所示,一种方案为: >指定所示的 5 个子区域为绿地。 >相邻至少两片绿地的 (2,2),(3,2) 和 (3,4) 演变为绿地。 >相邻两片绿地的 (3,3) 演变为绿地。 ![image.png](https://pic.leetcode-cn.com/1662692503-ncjywh-image.png){:width=500px} **示例 2:** >输入:`size = 2` >输出:`[[1,1],[2,1],[2,3]]` >解释:如下图所示: >指定所示的 3 个子区域为绿地。 >相邻三片绿地的 (2,2) 演变为绿地。 ![image.png](https://pic.leetcode-cn.com/1662692507-mgFXRj-image.png){:width=276px} **提示:** - `1 <= size <= 1000`

Submission

运行时间: 82 ms

内存: 59.2 MB

class Solution:
    def sandyLandManagement(self, size: int) -> List[List[int]]:



        # public solution ... 
        """
        贪心法:

        首先,三角形的三个角都必须要种树,因为这三个位置只与另外一个三角形相邻。
        
        接下来,从贪心的角度除法,
        如果某个三角形已经与另一个绿洲三角形相邻,那么不到万不得已就不应该在这个三角形中种树,
        因为这样一来就浪费掉了这两个三角形相邻的那条边。方便起见,从底边开始向上考虑。

        首先,底边应该每隔一个空位种树,也就是在底边的所有正三角形种树。
        接下来,底边的所有倒三角形都会在相邻的两个正三角形的影响下变为绿洲。

        接下来对于倒数第二行,所有正三角形的底边与最后一行倒三角形的顶边相邻,因此原则上不应该在这些位置上种树。
        对于倒数第二行的倒三角,第一个倒三角上应该种树。第一个倒三角种上树之后,第一个正三角以及第二个正三角都会变为绿洲,
        因此第二个倒三角不应该种树,而是应该在第三个倒三角种树,
        通过第三个倒三角将第三个正三角变为绿洲,
        然后通过第二个和第三个正三角将第二个倒三角变为绿洲。

        更一般的,在这里可以使用一个标记near_flag表示对于当前处理的倒三角是否已经被相邻的正三角影响到一条边。
        若near_flag为true,那么就应该标记下一个倒三角,否则就标记当前倒三角,并将near_flag置为true。

        除此之外,有一种特殊情况就是来到当前行的末尾时,
        如果触发了near_flag,那么就应该标记当前倒三角正上方的正三角从而避免浪费,
        因此在具体处理是,应该是从倒数第二行开始,沿之字型从下往上进行填充。
        """

        if size == 1:
            return [[1, 1]]
        
        res = [[1, 1]]
        
        for j in range(1, 2*size, 2):
            res.append([size, j])
        
        if size == 2:
            return res
        
        i = size - 1
        while i > 1:
            res.append([i, 2])
            
            if i - 1 == 1:
                return res
            
            for j in range(3, 2*(i - 1), 2):
                res.append([i - 1, j])
            
            if i - 2 == 1:
                return res
            
            res.append([i - 2, 1])
            
            if i - 3 == 1:
                return res
            
            for j in range(1, 2*(i - 3), 2):
                res.append([i - 3, j])
            
            i -= 4
        
        return res


Explain

题解采用了一种贪心策略结合特定模式的填充来减少初始种植的子区域数量。首先确保三角形的三个角都种植沙柳树,因为这些位置只与一个其他三角形相邻,无法通过邻接转化为绿地。接着,从三角形的底部开始,采用间隔填充的方式种植,使得每个底部的倒三角形能够由两侧的正三角形转化。对于接下来的每一行,通过交替跳过方式进行种植,确保最大化利用已有的绿地进行转化。这种方法从底部向上逐层处理,直到处理完所有行或达到需要的绿地覆盖。

时间复杂度: O(size^2)

空间复杂度: O(size)

class Solution:
    def sandyLandManagement(self, size: int) -> List[List[int]]:
        if size == 1:
            return [[1, 1]]  # 当只有一个子区域时,直接返回
        
        res = [[1, 1]]  # 初始化结果,包含顶点
        
        for j in range(1, 2*size, 2):
            res.append([size, j])  # 底边的所有正三角形种树
        
        if size == 2:
            return res  # 如果边长为2,直接返回结果
        
        i = size - 1
        while i > 1:
            res.append([i, 2])  # 在当前行的第一个位置种树
            
            if i - 1 == 1:
                return res  # 如果只剩一行,则返回结果
            
            for j in range(3, 2*(i - 1), 2):
                res.append([i - 1, j])  # 为当前行的倒三角形种树
            
            if i - 2 == 1:
                return res  # 如果只剩一行,则返回结果
            
            res.append([i - 2, 1])  # 为下一行的第一个位置种树
            
            if i - 3 == 1:
                return res  # 如果只剩一行,则返回结果
            
            for j in range(1, 2*(i - 3), 2):
                res.append([i - 3, j])  # 为下一行的每个正三角形种树
            
            i -= 4  # 向上移动四行
        
        return res

Explore

在三角形的三个角种植沙柳树是因为这些角落的位置特殊,每个角只与一个其他三角形相邻。这意味着如果不在这些角上种植,它们不能依靠周围的绿地进行转化,因为没有足够的相邻区域来影响它们变成绿地。因此,直接在这些关键位置种植可以确保整个区域的转化效率最大化,同时避免出现无法被转化的孤立区域。

间隔填充方式通过在每一行的特定位置种植沙柳树,确保可以利用已种植的树木进行最大化的区域转化。具体地,在底边的每个正三角形种树后,对于上一行的倒三角形,通过在两侧的正三角形间隔位置种树,这样每个倒三角形都至少与两个已种植的正三角形相邻。这种相邻关系使得倒三角形可以通过两侧的正三角形转化成绿地,实现整体的连续覆盖和转化。

对于边长为2的三角形,输出结果为包含顶点和底边的所有正三角形种树,即 [[1, 1], [2, 1], [2, 3]]。在这种情况下,底边的两个正三角形以及顶点的单个区域已经足够覆盖整个三角形的转化需求,每个区域都与至少一个种植区相邻,因此不需要额外的种植。这样保证了最小的种植数量同时实现区域的完全覆盖。