切披萨的方案数题解
切披萨方案数题解
原题
查看原题
给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ (表示苹果)和 ‘.’ (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。
切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。
请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。
示例 1:
1 | 输入:pizza = ["A..","AAA","..."], k = 3 |
示例 2:
1 | 输入:pizza = ["A..","AA.","..."], k = 3 |
示例 3:
1 | 输入:pizza = ["A..","A..","..."], k = 1 |
提示:
1 | 1 <= rows, cols <= 50 |
分析
前置说明
为了描述方便,我们用 披萨(i, j)
表示左上角坐标为 (i, j) 右下角坐标为(rows -1 ,cols-1) 的披萨
使用 dp 变量表示状态
dp[i][j][k]
表示 披萨(i, j)
切 k 次 分给 k+1个人 一共有多少种情况
拿例一中的图举例:
dp[2][2][0] == 0
表示下图的红框中的披萨切0次,分给1个人,共有 0 种情况。
dp[1][1][1] == 1
表示下图红框中的披萨切1次,分给 2个人,共有1情况
题目中所求的即为 dp[0][0][k-1]
的值,他代表的意思为,从 披萨(0, 0)
中切 k-1 次分给 k 个人的情况数
状态转移方程
dp 算法的关键是,如何将一个问题转化为子问题进行求解,即写出状态转移方程。
公式
$$dp[i][j][k] = \sum_{r=i+1}^{rows-1}(hasApple(i,j,r,j) ? dp[r][j][k-1] : 0) + \sum_{r=j+1}^{cols -1}(hasApple(i,j,i,r) ? dp[i][r][k-1] : 0)$$
其中:
$$hasApple(i,j,i,j
) = remains[i]][j] - reamains[i][j
] > 0$$
解释
dp[i][j][k]
对于任意的 披萨 (i, j),我们遍历他们所有的切割方法(包括横切和纵切),记每种切割完后剩余的披萨为 (i', j')
,则剩下的披萨的切割情况数可以用dp[i'][j'][k-1]
来表示,我们将所有的这些满足条件的子情况数加起来,就得到了dp[i][j][k]
hasApple
代表切下来的部分是否含有苹果。
remains[i][j]
数组表示披萨 (i, j)
包含的苹果总数
记切除后的披萨为 披萨(i', j')
则切下来的部分的披萨包含苹果数就可以用 remains[i][j] - remains[i'][j']
来表示了
如何求 remains 数组
我们观察上图,remains[x][y]
的值就等于 他本身 + 绿色的框 + 蓝色的框 - 红色的框(重复叠加了)
即 $remains[x][y] = current + remains[x+1][y] + remains[x][y+1] - remains[x+1][y+1] $
通过上述dp转移方程,我们算出披萨中的每个点的remains值存在数组中备用。
代码
1 | var ways = function (pizza, k) { |