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))) % MOD
            nr, nc = FisrtApple(r, c+1)
            vertical = (dfs(nr, nc, remain)[1] + (nc-c)*sum(dfs(nr, nc, remain - 1))) % MOD
            table[remain][r, c] = (across, vertical)
            return (across, vertical)
        
        r0, c0 = FisrtApple(0, 0)
        return sum(dfs(r0, c0, k-1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值