【每日一题Day297】LC1444切披萨的方案数 | 动态规划+二维前缀和

简介: 【每日一题Day297】LC1444切披萨的方案数 | 动态规划+二维前缀和

切披萨的方案数【LC1444】

给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: 'A' (表示苹果)和 '.' (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。

切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。

请你返回确保每一块披萨包含 至少一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。

能独立写出2100+的题啦,保持手感!

dfs+记忆化
  • 思路

image.png

递归边界:


k  为0无需分割时,检验这块披萨的苹果数目,如果大于0,则为有效分割,返回1;否则返回0


实现

class Solution {
    int n, m;
    int[][] sum;
    int[][][] memo;
    public static final int MOD = (int)(1e9 + 7);
    public int ways(String[] pizza, int k) {
        // 二维前缀和 + 记忆化
        n = pizza.length;
        m = pizza[0].length();
        sum = new int[n + 1][m + 1];// 以(x,y)为左上角、以(n-1, m-1)为右下角的区域包含的苹果数目
        memo = new int[n][m][k];
        for (int i = n - 1; i >= 0; i--){
            for (int j = m - 1; j >= 0; j--){
                sum[i][j] = sum[i + 1][j] + sum[i][j + 1] - sum[i + 1][j + 1];
                if ('A' == pizza[i].charAt(j)){
                    sum[i][j] += 1;
                }
                Arrays.fill(memo[i][j], -1);
            }
        }
        return dfs(0, 0, k - 1);
    }
    // 在以(x,y)为左上角、以(n-1, m-1)为右下角的区域,切k刀确保每一块披萨包含至少一个苹果的切披萨方案数
    public int dfs(int x, int y, int k){
        if (k == 0){
            return sum[x][y] > 0 ? 1 : 0;
        }
        if (memo[x][y][k] != -1) return memo[x][y][k];
        int res = 0;
        // 枚举行
        for (int i = x; i < n - 1; i++){
            if (sum[x][y] - sum[i + 1][y] > 0){// 这块披萨为x行至i行
                res = (res + dfs(i + 1, y, k- 1)) % MOD;
            }
        }
        // 枚举列
        for (int i = y; i < m - 1; i++){
            if (sum[x][y] - sum[x][i + 1] > 0){// 这块披萨为y列至i列
                res = (res + dfs(x, i + 1, k- 1)) % MOD;
            }
        }
        memo[x][y][k] = res;
        return res;
    }
}

image.png

dp
  • 实现
    从小到大枚举k
class Solution {
    public static final int MOD = (int)(1e9 + 7);
    public int ways(String[] pizza, int k) {
        // 二维前缀和 + 记忆化
        int n = pizza.length;
        int m = pizza[0].length();
        int[][] sum = new int[n + 1][m + 1];// 以(x,y)为左上角、以(n-1, m-1)为右下角的区域包含的苹果数目
        int[][][] dp = new int[n][m][k];
        for (int i = n - 1; i >= 0; i--){
            for (int j = m - 1; j >= 0; j--){
                sum[i][j] = sum[i + 1][j] + sum[i][j + 1] - sum[i + 1][j + 1];
                if ('A' == pizza[i].charAt(j)){
                    sum[i][j] += 1;
                }
            }
        }
        for (int c = 0; c < k; c++){
            for (int x = 0; x < n; x++){
                for (int y = 0; y < m; y++){
                    if (c == 0){
                        dp[x][y][0] = sum[x][y] > 0 ? 1 : 0;
                        continue;
                    }
                    for (int i = x; i < n - 1; i++){
                        if (sum[x][y] - sum[i + 1][y] > 0){
                            dp[x][y][c] = (dp[x][y][c] + dp[i + 1][y][c - 1]) % MOD;
                        }
                    }
                    for (int i = y; i < m - 1; i++){
                        if (sum[x][y] - sum[x][i + 1] > 0){
                            dp[x][y][c] = (dp[x][y][c] + dp[x][i + 1][c - 1]) % MOD;
                        }
                    }
                }
            }
        }
        return dp[0][0][k - 1];
    }
}

image.png

目录
相关文章
|
8月前
【每日一题Day312】LC2240买钢笔和铅笔的方案数 | 完全背包 数学
【每日一题Day312】LC2240买钢笔和铅笔的方案数 | 完全背包 数学
65 0
|
5月前
|
算法 C++
第一周算法设计与分析 E : 构造回文串
这篇文章介绍了解决算法问题"构造回文串"的方法,即判断给定的整数N(视为字符串)是否可以通过在前面添加任意个0(或不添加)来构造一个回文串,并给出了相应的C++代码实现。
|
8月前
|
人工智能 算法
前缀和算法题(区间次方和、小蓝平衡和、大石头的搬运工、最大数组和)
前缀和算法题(区间次方和、小蓝平衡和、大石头的搬运工、最大数组和)
|
8月前
【每日一题Day160】LC1092最短公共超序列 | 动态规划
【每日一题Day160】LC1092最短公共超序列 | 动态规划
63 0
【每日一题Day160】LC1092最短公共超序列 | 动态规划
|
8月前
【每日一题Day350】LC2652倍数求和 | 数学+容斥原理
【每日一题Day350】LC2652倍数求和 | 数学+容斥原理
50 0
|
8月前
【每日一题Day255】LC2679矩阵中的和 | 排序
【每日一题Day255】LC2679矩阵中的和 | 排序
33 0
|
算法
【算法挨揍日记】day03——双指针算法_有效三角形的个数、和为s的两个数字
【算法挨揍日记】day03——双指针算法_有效三角形的个数、和为s的两个数字
62 0
|
存储
【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索
子问题、哪些操作会影响数据:余下的甜甜圈数量left,以及剩余可以选的元素个数 cnt[x]【dfs函数的两个参数->使用状态压缩至一个int类型变量中】
124 0
【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索
|
机器学习/深度学习 存储 算法
【每日一题Day78】LC1803统计异或值在范围内的数对有多少 | 字典树+容斥原理
不过如果在工程中,不考虑前缀匹配的话,基本上使用 hash 就能满足。如果考虑前缀匹配的话,工程也不会使用 Trie 。一方面是字符集大小不好确定,另外,对于个别的超长字符 Trie 会进一步变深。
95 0
【每日一题Day78】LC1803统计异或值在范围内的数对有多少 | 字典树+容斥原理
|
项目管理
求组合数(模板)【组合数学】
求组合数(模板)【组合数学】
161 0
求组合数(模板)【组合数学】