1444. 切披萨的方案数
给你一个 rows x cols
大小的矩形披萨和一个整数 k
,矩形包含两种字符: 'A'
(表示苹果)和 '.'
(表示空白格子)。你需要切披萨 k-1
次,得到 k
块披萨并送给别人。
切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。
请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。
思路
切披萨竖着切不影响行数,横着切不影响列数。
我们先定义一个dp[k][i][j]
表明第i行、第j列构成的右下方披萨切成k块的方案数,则dp[k][i][j]
等于dp[k - 1][i1][j]
加上dp[k - 1][i][j - 1]
数量的总和,其中i1为大于i小于n的任意数,j1为大于j小于m的任意数,并且新切出来的披萨中要有苹果。
我们定义apples[i][j]
表示第i行、第j列构成的右下方披萨包含的苹果数量,则apples[i][j] = (chars[i][j] == 'A' ? 1 : 0) + apples[i + 1][j] + apples[i][j + 1] - apples[i + 1][j + 1];
当apples[i][j] > apples[i1][j] 或者apples[i][j] > apples[i][j1]
时,新切出来的披萨中一定有苹果。
代码
public class Solution {
public int ways(String[] pizza, int k) {
int n = pizza.length;
int m = pizza[0].length();
var mod = (int) 1e9 + 7;
char[][] chars = new char[n][m];
for (int i = 0; i < n; i++) {
chars[i] = pizza[i].toCharArray();
}
var apples = new int[n + 1][m + 1];
// 表示坐标i j 包含所构成的右下方披萨切成k块的方案数量
var dp = new long[k + 1][n][m];
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
apples[i][j] = (chars[i][j] == 'A' ? 1 : 0) + apples[i + 1][j] + apples[i][j + 1] - apples[i + 1][j + 1];
dp[1][i][j] = apples[i][j] > 0 ? 1 : 0;
}
}
// dp[k][i][j] += k-1时,竖着切的总和加上横着切的总和
for (int k1 = 2; k1 <= k; k1++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 竖着切
for (int j1 = j + 1; j1 < m; j1++) {
if (apples[i][j] > apples[i][j1]) {
dp[k1][i][j] += dp[k1 - 1][i][j1];
dp[k1][i][j] %= mod;
}
}
// 横着切
for (int i1 = i + 1; i1 < n; i1++) {
if (apples[i][j] > apples[i1][j]) {
dp[k1][i][j] += dp[k1 - 1][i1][j];
dp[k1][i][j] %= mod;
}
}
}
}
}
return (int)dp[k][0][0];
}
}