【力扣roadmap】2209. 用地毯覆盖后的最少白色砖块

14 阅读3分钟

题目描述

image.png

思路

乍一看出现了类似位的操作,让人以为是状压dp。 但放心宝子,这不是状压dp,只是一个普通dp。

有点像背包的思想:目前我们手上有i块地毯,我们思考第j块地砖是否进行覆盖(你别管这个是黑地砖还是白地砖,反正你就思考覆盖的事情就好),如果第j块地砖你选择了覆盖,那么接下来的局面变成了“我们手上有i-1块地毯,我们思考j-carpetlen是否进行覆盖”;如果第j块地砖你不覆盖,那么接下来的局面变成了“我们手上还有i块地毯,我们思考j-1是否进行覆盖(当然这种情况下,如果j块地砖是白的,那么必然会让最少白砖数+1)”。

找到了这个题可以用将一个大问题转成规模更小的问题,所以我们可以试试dp。你发现我们在上面的分析中,追踪了两个信息,第一个是地毯数i,第二个是还需要考虑覆盖的地面长度j

我们定义二维数组dp,长度为numCarpet+1,宽度为n(地面长度)。那么根据上面的分析,我们会有以下状态转移方程:

dp[i][j]=min(dp[i][j1]+int(floor[j]),dp[i1][jcarpetlen])dp[i][j] = min(dp[i][j-1] + int(floor[j]) , dp[i-1][j-carpetlen])

dp[i][j]会从两个状态的最小值中转移过来,第一情况是不覆盖第j块砖,那么你需要承担这个砖是白色的代价(加上这个砖的数值,白色就对应1,黑色对应0),第二情况是覆盖第j块砖,但是地毯数就会少一个。

最后补充一点的是,内部for循环你会去循环地面开始铺盖的起始点对吗?但你想到你的地毯长度是carpetlen,所以你在选择开始铺盖的起始点的时候,从0开始,合适吗?不太合适吧,假如你选择铺盖j,那么j-carpetlen就是负数了,换言之这里进行铺盖是invalid。你应该至少从下标为carpetlen开始进行铺盖,这代表着[0,carpetlen-1]这个长度为carpetlen的地面被覆盖了且dp[i][0~carpetlen - 1] = 0 但你再细想一下,此时你手上有i块地毯,那你起码能盖住i * carpetlen的地面(不管地面原本是啥颜色,你就是能盖住这些地面),所以但凡你枚举的j小于i * carpetlen,这些情况对外暴露的白色砖数都是0。所以进一步优化,你可以从下标为i * carpetlen进行遍历,这表明[0,carpetlen * i - 1]这个区间被覆盖且dp[i][0~carpetlen * i - 1] = 0

代码

class Solution:
    def minimumWhiteTiles(self, floor: str, numCarpets: int, carpetLen: int) -> int:
        floor = list(map(int,floor))
        n = len(floor)  

        dp = [[0] * n for _ in range(numCarpets+1)] 
        dp[0] = list(accumulate(floor)) 
        for i in range(1,numCarpets+1):
            for j in range(carpetLen * j , n) : # 从carpet * j 开始遍历而不是从carpet开始遍历 这是一个小优化
                dp[i][j] = min(dp[i][j-1] + floor[j] , dp[i-1][j-carpetLen])
        return dp[-1][-1]