leetcode--切披萨的方案数

 题目是LeetCode第188场周赛的第三题,链接:1444. 切披萨的方案数。具体描述见原题。

 这道题是一道比较麻烦的动态规划题目,因为是三维的动态规划。先定义dp[i][j][p]:切了p-1刀之后(这时得到p块披萨)剩下以(i,j)为左上角的披萨的方案数。则对于第p-2刀应该切在哪有了限制,因为需要保证切了之后剩下的那块披萨得可以再切一刀得到两块披萨(都得有苹果)。然后就是这第p-2刀切了之后剩下什么同样有限制,这剩下的必然是以(ii,j)(i,jj)为左上角的披萨,其中iijj分别是比ij小的坐标,也就是不可能剩下一块以(ii,jj)为左上角的披萨,否则没法通过只切一刀就得到以(i,j)为左上角的披萨。那么现在就可以写出递推公式了

d p [ i ] [ j ] [ p ] = ∑ i i = 0 i − 1 d p [ i i ] [ j ] [ p − 1 ] + ∑ j j = 0 j − 1 d p [ i ] [ j j ] [ p − 1 ] dp[i][j][p]=\sum_{ii=0}^{i-1}dp[ii][j][p-1]+\sum_{jj=0}^{j-1}dp[i][jj][p-1] dp[i][j][p]=ii=0i1dp[ii][j][p1]+jj=0j1dp[i][jj][p1]

 但是上面的iijj都需要满足特定条件,比如iicumSum[i][j] > 0 && cumSum[ii][j] - cumSum[i][j] > 0,即切了第p-2刀之后剩下的第p-1块披萨可以被再切一刀形成两块有苹果的披萨,同理对jcumSum[i][j] > 0 && cumSum[i][jj] - cumSum[i][j] > 0。这里的cumSum[i][j]的意思是以(i,j)为左上角的披萨里有多少苹果。

 最后需要返回的结果其实就是将所有dp[i][j][k-1]给加起来。

 时间复杂度为 O ( ( m + n ) m n k ) O((m+n)mnk) O((m+n)mnk),空间复杂度为 O ( m n k ) O(mnk) O(mnk)(可以继续优化空间复杂度,懒得做了)。

 JAVA版代码如下:

class Solution {
    
    public int ways(String[] pizza, int k) {
        int mod = 1000_000_007;
        int row = pizza.length;
        int col = pizza[0].length();
        // cumSum[i][j]:以(i,j)为左上角的披萨里有多少个苹果
        int[][] cumSum = new int[row][col];
        cumSum[row - 1][col - 1] = pizza[row - 1].charAt(col - 1) == 'A' ? 1 : 0;
        for (int j = col - 2; j >= 0; --j) {
            if (pizza[row - 1].charAt(j) == 'A') {
                cumSum[row - 1][j] = cumSum[row - 1][j + 1] + 1;
            }
            else {
                cumSum[row - 1][j] = cumSum[row - 1][j + 1];
            }
        }
        for (int i = row - 2; i >= 0; --i) {
            int curcum = 0;
            for (int j = col - 1; j >= 0; --j) {
                curcum += pizza[i].charAt(j) == 'A' ? 1 : 0;
                cumSum[i][j] = cumSum[i + 1][j] + curcum;
            }
        }

        // dp[i][j][p]:切了p-1刀后剩下第p块且其以(i,j)为左上角的披萨的方案数
        int[][][] dp = new int[row][col][k];
        dp[0][0][0] = 1;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                // 只需要切k-1次就得到k块
                for (int p = 1; p < k; ++p) {
                    if (cumSum[i][j] == 0) {
                        break;
                    }
                    // 第p-2刀切了之后剩下的是(i, jj)
                    for (int jj = 0; jj < j; ++jj) {
                        // 需要确保切了第p-2刀之后还可以再竖切一刀剩下(i,j)
                        if (cumSum[i][j] > 0 && cumSum[i][jj] - cumSum[i][j] > 0) {
                            dp[i][j][p] = (dp[i][j][p] + dp[i][jj][p - 1]) % mod;
                        }
                    }
                    // 第p-2刀切了之后剩下的是(ii, j)
                    for (int ii = 0; ii < i; ++ii) {
                        if (cumSum[i][j] > 0 && cumSum[ii][j] - cumSum[i][j] > 0) {
                            // 需要确保切了第p-2刀之后还可以再切一刀剩下(i,j)
                            dp[i][j][p] = (dp[i][j][p] + dp[ii][j][p - 1]) % mod;
                        }
                    }
                }
            }
        }
        int result = 0;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                result = (result + dp[i][j][k - 1]) % mod;
            }
        }
        return result;
    }
}

 提交结果如下:


 Python版代码如下:

class Solution:
    def ways(self, pizza: List[str], k: int) -> int:
        row, col = len(pizza), len(pizza[0])
        cumSum = [[0 for _ in range(col)] for _ in range(row)]
        cumSum[row - 1][col - 1] = 1 if pizza[row - 1][col - 1] == 'A' else 0
        for j in range(col - 2, -1, -1):
            cumSum[row - 1][j] += cumSum[row - 1][j + 1] + (1 if pizza[row - 1][j] == 'A' else 0)
        for i in range(row - 2, -1, -1):
            cur = 0
            for j in range(col - 1, -1, -1):
                cur += 1 if pizza[i][j] == 'A' else 0
                cumSum[i][j] += cur + cumSum[i + 1][j]
        dp = [[[0 for _ in range(col)] for _ in range(row)] for _ in range(k)]
        dp[0][0][0] = 1 if cumSum[0][0] > 0 else 0
        for p in range(1, k):
            for i in range(row):
                for j in range(col):
                    for ii in range(i):
                        if cumSum[i][j] > 0 and cumSum[ii][j] > cumSum[i][j]:
                            dp[p][i][j] += dp[p - 1][ii][j]
                    for jj in range(j):
                        if cumSum[i][j] > 0 and cumSum[i][jj] > cumSum[i][j]:
                            dp[p][i][j] += dp[p - 1][i][jj]
        return sum(dp[k - 1][i][j] for i in range(row) for j in range(col)) % 1000000007

 提交结果如下:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值