LeetCode 1444. 切披萨的方案数(一)

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

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

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

在这里插入图片描述
在这里插入图片描述
解题思路:
下面两张图展示了每次切披萨的时候面临的两种选择:水平切和竖直切(由于版面限制,呈现在两张图中)。阴影部分是一刀切下后,能即刻分到的披萨,每次切需要保证即刻分得的披萨内部“A”的数量至少为1(“A”即Apple)。第二张图中蓝色阴影内“A”数量为0,为不合法的切割方式。
切割后,剩余披萨是否合法,需要看所剩披萨内“A”的数目是否不小于尚未分得披萨人数,如果小于则不合法。
在这里插入图片描述
在这里插入图片描述
但可能存在重叠的子问题,增加算法时间复杂度,如下图。
在这里插入图片描述
采用一般的动态规划dp[index][iterator]思想。
dp[m][n][k]前两个维度是坐标,最后一个维度是cuts数,用来表示右下角的pizza切k cuts有多少种合法的切法。如果k=0,则表示一刀不切时,这块pizza是否合法(即是否至少含一个苹果),此时也是递归问题的base case
def dp[m][n][k]:
if k==0:return 1 if hasApple(pizza[m:M][n:N]) else 0.

class Solution {
public:
    int ways(vector<string>& pizza, int K) {
        int kMod=1e9+7;
        int M=pizza.size();
        int N=pizza[0].size();

        vector<vector<int>> presum(M+1,vector<int>(N+1));
        for(int i=0;i<M;i++){
            for(int j=0;j<N;j++){
                presum[i+1][j+1]=presum[i][j+1]+presum[i+1][j]-presum[i][j]+(pizza[i][j]=='A');
            }
        }//左上角前缀和
        auto hasApples=[&](int x1,int y1,int x2,int y2){
            return presum[x2+1][y2+1]-presum[x1][y2+1]-presum[x2+1][y1]+presum[x1][y1]>0;
        };//求解任意一个子矩阵是否有苹果

        vector<vector<vector<int>>> cache(M,vector<vector<int>> (N,vector<int>(K,-1)));
        
        function<int(int,int,int)> dp=[&](int m,int n,int k)->int{
            if(k==0)    return hasApples(m,n,M-1,N-1);
            int res=cache[m][n][k];
            if(res!=-1) return res;
            res=0;
            for(int x=m;x<M-1;x++){
                res=(res+hasApples(m,n,x,N-1)*dp(x+1,n,k-1))%kMod;
            }
            for(int y=n;y<M-1;y++){
                res=(res+hasApples(m,n,M-1,y)*dp(m,y+1,k-1))%kMod;
            }
            return res;
        };
        return dp(0,0,K-1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值