【力扣roadmap】1444. 切披萨的方案数

8 阅读2分钟

题目描述

image.png

思路

我们假设我们可以在O(1)时间内计算一个子矩阵内是否有苹果(这可以使用二维前缀和解决,但是我们现将这个问题放在一边,我们先考虑DP的事情)

整个矩阵是n行m列。假如下标从1开始,我们现在剩余c刀,对于左上角在(i,j)右下角在(n,m)的子矩阵而言:

  • 在第hc的位置切一刀:将[i:hc , j:m]子矩阵划分出去,剩余子矩阵为[hc+1:n , j:m]。那么问题变成了使用c-1刀切分[hc+1:n , j:m]

  • 在第vc的位置切一刀:将[i:n , j:vc]子矩阵划分出去,剩余子矩阵为[i:n , vc+1:m]。 那么问题变成了使用c-1刀切分[i:n , vc+1:m]

  • 递归边界就是c==0,此时检查剩余矩阵是否有元素。如果有就是一个合法的方案。

  • 可以切一刀的条件就是,你划分出去的子矩阵里面有元素,就可以切一刀。我们就加上这个子问题的答案。

  • 我们每次查询区间[x1:x2,y1:y2]子矩阵(x1行和x2行均在子矩阵中,y1列和y2列也均在子矩阵中)是否有元素,使用二维前缀和可以O(1)时间做到。

代码

class Solution:
    def ways(self, pizza: List[str], k: int) -> int:
        
        MOD = int(1e9) + 7
        n , m = len(pizza) , len(pizza[0]) 
        prefix2d = [ [0] * (m+1) for _ in range(n+1)] 


        for i in range(1,n+1) :
            for j in range(1,m+1) :
                prefix2d[i][j] = prefix2d[i-1][j] + prefix2d[i][j-1] - prefix2d[i-1][j-1] + (pizza[i-1][j-1] == 'A')
        
        def query(x1,y1,x2,y2): # 下标从1开始  [x1,y1][x2,y2]子矩阵闭区间
            return (prefix2d[x2][y2] - prefix2d[x2][y1-1] - prefix2d[x1-1][y2] + prefix2d[x1-1][y1-1]) > 0 

        @cache 
        def dfs(i : int , j : int , c : int) -> int :

            if c == 0 :
                return 1  if query(i,j,n,m) else 0 
            
            res = 0 
            for hc in range(i,n) :
                if query(i,j,hc,m) :
                    res = (res + dfs(hc+1,j,c-1)) % MOD

            for vc in range(j,m) :
                if query(i,j,n,vc) :
                    res = (res + dfs(i,vc+1,c-1)) % MOD

            return res % MOD  
            
        return dfs(1,1,k-1)