LeetCode 1444 - 切披萨的方案数(周赛)

题目描述

1444. 切披萨的方案数

解法一:DP(C++)

详细参考 雪融之时. 的解

统计可能数,一来挺容易行到用动态规划

状态: 我们每次切完都是接着处理剩下的部分,那么可以记 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示可能的切法, ( i , j ) (i, j) (i,j)表示剩余披萨的左上角为,当前披萨被切分为 k k k

转移方程: d p [ x ] [ y ] [ k + 1 ] = d p [ i ] [ j ] [ k ] i f ( 切 分 的 两 部 分 都 有 苹 果 ) dp[x][y][k+1] = dp[i][j][k]\ \ if(切分的两部分都有苹果) dp[x][y][k+1]=dp[i][j][k]  if(),其中记原来的左上角为 ( i , j ) (i, j) (i,j),切分后新的左上角为 ( x , y ) (x, y) (x,y)

对于是横切还是竖切,就通过穷举了

最后讲一下,怎么判断切分的部分上是否有苹果?这个思想还是挺有趣的

n u m s [ i ] [ j ] nums[i][j] nums[i][j] 表示以 ( 0 , 0 ) (0, 0) (0,0) 为左上角, ( i , j ) (i, j) (i,j)为右下角的矩形区域的苹果数,那么任一区域(左上角为 ( s r , s c ) (sr, sc) (sr,sc),右下角为 ( e r , e c ) (er,ec) (er,ec))包含的苹果数就可以通过 n u m s [ e r ] [ e c ] − n u m s [ s r − 1 ] [ e c ] − n u m s [ e r ] [ s c − 1 ] + n u m s [ s r − 1 ] [ s c − 1 ] nums[er][ec] - nums[sr-1][ec]-nums[er][sc-1]+nums[sr-1][sc-1] nums[er][ec]nums[sr1][ec]nums[er][sc1]+nums[sr1][sc1] 求得

typedef long long ll;class Solution {
public:int ways(vector<string>& pizza, int k) {const ll MOD = 1e9+7;int row = pizza.size(), col = pizza[0].length();vector<vector<int>> nums(row, vector<int>(col, 0));if(pizza[0][0]=='A') nums[0][0] = 1;for(int i=1;i<row;i++) nums[i][0] = nums[i-1][0]+(pizza[i][0]=='A');for(int j=1;j<col;j++) nums[0][j] = nums[0][j-1]+(pizza[0][j]=='A');for(int i=1;i<row;i++)for(int j=1;j<col;j++)nums[i][j] = nums[i-1][j]+nums[i][j-1]-nums[i-1][j-1]+(pizza[i][j]=='A');vector<vector<vector<ll>>> dp(row, vector<vector<ll>>(col, vector<ll>(k+1, 0)));dp[0][0][1] = 1;for(int x=2;x<=k;x++){for(int i=0;i<row;i++){for(int j=0;j<col;j++){if(!dp[i][j][x-1]) continue;;for(int z=i+1;z<row;z++){if(hasapple(nums, i, j, z-1, col-1)&&hasapple(nums, z, j, row-1, col-1)){dp[z][j][x] += dp[i][j][x-1];dp[z][j][x] %= MOD;}}for(int z=j+1;z<col;z++){if(hasapple(nums, i, j, row-1, z-1)&&hasapple(nums, i, z, row-1, col-1)){dp[i][z][x] += dp[i][j][x-1];dp[i][z][x] %= MOD;}}}}}ll ans= 0;for(int i=0;i<row;i++)for(int j=0;j<col;j++)ans += dp[i][j][k];ans %= MOD;return ans;}bool hasapple(vector<vector<int>>& nums, int sr, int sc, int er, int ec){int p1 = 0, p2 = 0, p3 = 0;if(sr!=0 && sc!=0) p1 = nums[sr-1][sc-1];if(sr!=0) p2 = nums[sr-1][ec];if(sc!=0) p3 = nums[er][sc-1];return nums[er][ec]-p2-p3+p1>0;
}};

解法二:记忆递归(Python)

详细参考 java_Lee的解

递归思路是很简单,就是我切完这一刀,我下一刀怎么切的事。关于切分来的部分怎么保证有🍎,java_Lee 用了一个表来记录第一个苹果出现的行和列,而且 java_Lee 也在题解中证明了切走左上无🍎的行列不影响递归结果,那这样的话,每次就对第一个🍎出现位置的右下部分进行切分,每次切分的结果都能保证有🍎了

至于记忆递归,就是将一些已经处理过的递归请款打表记录下来,下次遇到同样状况的递归直接查表

最后就是怎么切的问题,这里 java_Lee 主要把横切和竖切的情况分开来讨论了,每次递归都分别记录了横切可能的方案数和竖切可能的方案数

class Solution:def ways(self, pizza: List[str], k: int) -> int:MOD = int(1e9+7)row, col = len(pizza),  len(pizza[0])table = [{(row, col):(0,0)} for _ in range(k)]first_apple = [[None] * col for _ in range(row)]def FisrtApple(r, c):'''定义对first_apple的访问方法,避免越界'''return (row, col) if r>= row or c >= col else first_apple[r][c]for r in range(row)[::-1]:for c in range(col)[::-1]:first_apple[r][c] = (r, c) if pizza[r][c] == 'A' else tuple(map(min, zip(FisrtApple(r+1, c), FisrtApple(r, c+1))))if first_apple[r][c] != (row, col):table[0][first_apple[r][c]] = (1, )def dfs(r, c, remain):if (r, c) in table[remain]: return table[remain][r, c]nr, nc = FisrtApple(r+1, c)across = (dfs(nr, nc, remain)[0] + (nr-r)*sum(dfs(nr, nc, remain - 1))) % MODnr, nc = FisrtApple(r, c+1)vertical = (dfs(nr, nc, remain)[1] + (nc-c)*sum(dfs(nr, nc, remain - 1))) % MODtable[remain][r, c] = (across, vertical)return (across, vertical)r0, c0 = FisrtApple(0, 0)return sum(dfs(r0, c0, k-1)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部