【前缀和 记忆化搜索】LeetCode1444. 切披萨的方案数
本文涉及的基础知识点
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
动态规划 记忆化搜索
LeetCode1444. 切披萨的方案数
给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ (表示苹果)和 ‘.’ (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。
切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。
请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。
示例 1:
输入:pizza = [“A…”,“AAA”,“…”], k = 3
输出:3
解释:上图展示了三种切披萨的方案。注意每一块披萨都至少包含一个苹果。
示例 2:
输入:pizza = [“A…”,“AA.”,“…”], k = 3
输出:1
示例 3:
输入:pizza = [“A…”,“A…”,“…”], k = 1
输出:1
提示:
1 <= rows, cols <= 50
rows == pizza.length
cols == pizza[i].length
1 <= k <= 10
pizza 只包含字符 ‘A’ 和 ‘.’ 。
预处理
本题解点的坐标用(x,y)表示,而不是行列。
任何时间,待切的蛋糕一定保留原始蛋糕的右下角(cols-1,rows-1)。所以只需要枚举(left,top)。
垂直切:左边的部分,bottom一定和原始蛋糕bottom,相同。只需要枚举left,top,right。
水平且:上边的部分,right一定和原始蛋糕同。只需要枚举left,top,bottom。
vLeft[left][t][r] 记录(left,t,r,rows-1) 是否包括苹果。
vTop[left][[t][b] 记录(left,t,cols-1,b) 是否包括苹果。
计算vLeft的过程如下:
符合以下条件之一vLeft[left][t][r] 就有苹果:
一,(left,t)有苹果。
二,vLeft(left+1,t,r)有苹果。
三,vLeft(left,t+1,r)有苹果。
vTop类似。
m =max(rows,cols)。 空间复杂度O(mmm),时间复杂也是O(mmm)。
** 可以用二维前缀和计算左边(上边)的苹果数**。
动态规划
动态的状态表示
dp[k][left][top] 表示将蛋糕(left,top,cols-1,rows-1)切k次的方案数。
空间复杂度:O(k × \times × rows × \times × cols)
动态规划的转移方程
垂直切
dp[k][left][top] += F o r x : l e f t + 1 : c o l s − 1 v L e f t [ l e f t ] [ t o p ] [ x − 1 ] 有苹果 D p ( k − 1 , x , t o p ) \large For_{x:left+1:cols-1}^{vLeft[left][top][x-1]有苹果}Dp(k-1,x,top) Forx:left+1:cols−1vLeft[left][top][x−1]有苹果Dp(k−1,x,top)
记忆化搜索: Dp是函数,如果dp有对应值,则返回dp的值。否则更新dp的值,并返回。避免重复计算。
水平切:
dp[k][left][top] += F o r x : t o p + 1 : r o w s − 1 v T o p [ l e f t ] [ t o p ] [ x − 1 ] 有苹果 D p ( k − 1 , l e f t , x ) \large For_{x:top+1:rows-1}^{vTop[left][top][x-1]有苹果}Dp(k-1,left,x) Forx:top+1:rows−1vTop[left][top][x−1]有苹果Dp(k−1,left,x)
注意:送出的部分必须有一个苹果,包括的部分必须有k个苹果。
时间复杂度:O(k × \times × rows × \times × cols × \times × (rows+cols))
动态规划的初始值
dp[0][left][top] = (left,top,m_iC-1,m_iR-1)是否有苹果。 如果是记忆化搜索,还需要vHasDo 记录各状态是否处理。
动态规划的填表顺序
直接Do(k-1,0,0) 是记忆化搜索。从1到k 计算所有状态的值,是动态规划。记忆化搜索可以避免计算一些不需要计算的值。
动态规划的返回值
dp[k][0][0]
代码
核心代码
template<int MOD = 1000000007>
class C1097Int
{
public:C1097Int(long long llData = 0) :m_iData(llData% MOD){}C1097Int operator+(const C1097Int& o)const{return C1097Int(((long long)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((long long)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = (m_iData + MOD - o.m_iData) % MOD;return *this;}C1097Int operator-(const C1097Int& o){return C1097Int((m_iData + MOD - o.m_iData) % MOD);}C1097Int operator*(const C1097Int& o)const{return((long long)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((long long)m_iData * o.m_iData) % MOD;return *this;}C1097Int operator/(const C1097Int& o)const{return *this * o.PowNegative1();}C1097Int& operator/=(const C1097Int& o){*this /= o.PowNegative1();return *this;}bool operator==(const C1097Int& o)const{return m_iData == o.m_iData;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(long long n)const{C1097Int iRet = 1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}int ToInt()const{return m_iData;}
private:int m_iData = 0;;
};template<class T = int>
class CPreSum2 {
public:template<class _Pr>CPreSum2(int rowCnt, int colCount, _Pr pr):m_iRowCnt(rowCnt),m_iColCnt(colCount){m_vSum.assign(rowCnt + 1, vector<int>(colCount + 1));for (int r = 0; r < rowCnt; r++) {for (int c = 0; c < colCount; c++) {m_vSum[r + 1][c + 1] = m_vSum[r][c + 1] + m_vSum[r + 1][c] - m_vSum[r][c] + pr(r, c);}}}T Get(int left, int top, int right, int bottom)const {return m_vSum[bottom + 1][right + 1] - m_vSum[top][right + 1] - m_vSum[bottom + 1][left] + m_vSum[top][left];}T GetTopLeft(int left, int top) { return Get(left, top, m_iColCnt - 1, m_iRowCnt - 1); }vector<vector<T>> m_vSum;const int m_iRowCnt, m_iColCnt;
};
class Solution {
public:int ways(vector<string>& pizza, int k) {m_iR = pizza.size();m_iC = pizza[0].size();CPreSum2<int> preSum(m_iR, m_iC, [&](int r, int c) {return 'A' == pizza[r][c]; }); m_dp.assign(k, vector<vector<C1097Int<>>>(m_iC, vector<C1097Int<>>(m_iR)));m_vHasDo.assign(k, vector<vector<bool>>(m_iC, vector<bool>(m_iR)));for (int left = 0; left < m_iC; left++) {for (int top = 0; top < m_iR; top++) {m_dp[0][left][top] = preSum.GetTopLeft(left, top)>0;m_vHasDo[0][left][top] = true;}}return Rec(preSum, pizza, k-1, 0, 0).ToInt();}C1097Int<> Rec( CPreSum2<int>& preSum, vector<string>& pizza, int k, int left, int top) {auto& iRet = m_dp[k][left][top];if (m_vHasDo[k][left][top]) { return iRet; }m_vHasDo[k][left][top] = true;int cnt = preSum.Get(left, top, m_iC - 1, m_iR - 1);//当前蛋糕有多少苹果for (int x = left + 1; x <= m_iC - 1; x++) {//垂直切const int iSub = preSum.Get(left, top, x - 1, m_iR - 1);//送出多少苹果if (0 == iSub ) { continue; }if (cnt - iSub < k ) { continue; }iRet += Rec(preSum, pizza, k - 1, x, top);}for (int x = top + 1; x <= m_iR - 1; x++) {//水平切const int iSub = preSum.Get(left, top, m_iC - 1, x - 1);if (0 == iSub) { continue; }if (cnt - iSub < k) { continue; }iRet += Rec(preSum, pizza, k - 1, left, x);}return iRet;}vector<vector<vector<C1097Int<> >>> m_dp;vector<vector<vector<bool >>> m_vHasDo;int m_iR, m_iC;
};
VS自带单元测试
template<int MOD = 1000000007>
class C1097Int
{
public:C1097Int(long long llData = 0) :m_iData(llData% MOD){}C1097Int operator+(const C1097Int& o)const{return C1097Int(((long long)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((long long)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = (m_iData + MOD - o.m_iData) % MOD;return *this;}C1097Int operator-(const C1097Int& o){return C1097Int((m_iData + MOD - o.m_iData) % MOD);}C1097Int operator*(const C1097Int& o)const{return((long long)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((long long)m_iData * o.m_iData) % MOD;return *this;}C1097Int operator/(const C1097Int& o)const{return *this * o.PowNegative1();}C1097Int& operator/=(const C1097Int& o){*this /= o.PowNegative1();return *this;}bool operator==(const C1097Int& o)const{return m_iData == o.m_iData;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(long long n)const{C1097Int iRet = 1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}int ToInt()const{return m_iData;}
private:int m_iData = 0;;
};template<class T = int>
class CPreSum2 {
public:template<class _Pr>CPreSum2(int rowCnt, int colCount, _Pr pr):m_iRowCnt(rowCnt),m_iColCnt(colCount){m_vSum.assign(rowCnt + 1, vector<int>(colCount + 1));for (int r = 0; r < rowCnt; r++) {for (int c = 0; c < colCount; c++) {m_vSum[r + 1][c + 1] = m_vSum[r][c + 1] + m_vSum[r + 1][c] - m_vSum[r][c] + pr(r, c);}}}T Get(int left, int top, int right, int bottom)const {return m_vSum[bottom + 1][right + 1] - m_vSum[top][right + 1] - m_vSum[bottom + 1][left] + m_vSum[top][left];}T GetTopLeft(int left, int top) { return Get(left, top, m_iColCnt - 1, m_iRowCnt - 1); }vector<vector<T>> m_vSum;const int m_iRowCnt, m_iColCnt;
};
class Solution {
public:int ways(vector<string>& pizza, int k) {m_iR = pizza.size();m_iC = pizza[0].size();CPreSum2<int> preSum(m_iR, m_iC, [&](int r, int c) {return 'A' == pizza[r][c]; }); m_dp.assign(k, vector<vector<C1097Int<>>>(m_iC, vector<C1097Int<>>(m_iR)));m_vHasDo.assign(k, vector<vector<bool>>(m_iC, vector<bool>(m_iR)));for (int left = 0; left < m_iC; left++) {for (int top = 0; top < m_iR; top++) {m_dp[0][left][top] = preSum.GetTopLeft(left, top)>0;m_vHasDo[0][left][top] = true;}}return Rec(preSum, pizza, k-1, 0, 0).ToInt();}C1097Int<> Rec( CPreSum2<int>& preSum, vector<string>& pizza, int k, int left, int top) {auto& iRet = m_dp[k][left][top];if (m_vHasDo[k][left][top]) { return iRet; }m_vHasDo[k][left][top] = true;int cnt = preSum.Get(left, top, m_iC - 1, m_iR - 1);//当前蛋糕有多少苹果for (int x = left + 1; x <= m_iC - 1; x++) {//垂直切const int iSub = preSum.Get(left, top, x - 1, m_iR - 1);//送出多少苹果if (0 == iSub ) { continue; }if (cnt - iSub < k ) { continue; }iRet += Rec(preSum, pizza, k - 1, x, top);}for (int x = top + 1; x <= m_iR - 1; x++) {//水平切const int iSub = preSum.Get(left, top, m_iC - 1, x - 1);if (0 == iSub) { continue; }if (cnt - iSub < k) { continue; }iRet += Rec(preSum, pizza, k - 1, left, x);}return iRet;}vector<vector<vector<C1097Int<> >>> m_dp;vector<vector<vector<bool >>> m_vHasDo;int m_iR, m_iC;
};
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
---|
《喜缺全书算法册》以原理、正确性证明、总结为主。 |
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。
相关文章:

【前缀和 记忆化搜索】LeetCode1444. 切披萨的方案数
本文涉及的基础知识点 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 动态规划 记忆化搜索 LeetCode1444. 切披萨的方案数 给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ ÿ…...

异常处理1--5.31
try--catch--finally格式: try{ }catch(错误类型){ }finally{ } 执行情况: package javatest5;public class test {public static void main(String[] args) {int num115;int num20;try{System.out.println("商等于:"num1/num…...

AI免费插件 批量条码大师,支持100多种条码类型
没想到在网上看到一款和之前 悟空条码 类似的条码插件,叫批量条码大师,他做的比 悟空条码 功能更强,界面更美观,特分享出来给大家。 本插件采用了BWIPJS条码库,支持110种条码、二维码的生成; 支持批量生成,…...

使用 Django 连接 MySQL 数据库
文章目录 步骤一:安装必要的库和驱动步骤二:配置数据库连接步骤三:执行数据库迁移步骤四:开始使用 MySQL 数据库创建一个模型迁移模型到数据库使用模型进行数据操作创建新记录:查询记录:更新记录࿱…...

【谷歌开发者月刊】五月内容汇总,Google I/O 点燃您的创新灵感
五月因 Google I/O 大会的成功举办而备受瞩目,我们在为开发者们带来技术更新的同时,更提供了丰富的学习资源。一起在本期月刊中回顾精彩内容,带着学习热情继续探索技术的更新与发展吧! 本月看点 精彩看点多多,请上下滑…...

wpf 实现长时间未操作自动退出登录
思路 在WPF应用程序中实现长时间未操作自动退出,主要思路是先收集用户操作行为,用一个全局变量记录用户最后操作时间,再用一个异步线程去监控用户的最后操作时间和当前时间进行比较如果超过了设定时间认为长时间未操作自动退出登录并显示相关…...

pytorch学习笔记3
标量数据 标量数据通常指的是单个数值,比如一个损失值、准确率、学习率等。这些单个的数值数据可以被记录下来,用于分析模型的训练过程、性能等。在代码中,writer.add_scalar() 的作用就是将这些标量数据添加到日志中,以便后续在可…...

开源监控工具monit安装部署
Monit 简介 Monit是一个轻量级(500KB)跨平台的用来监控Unix/linux系统的开源工具。部署简单,并且不依赖任何第三方程序、插件或者库。 Monit可以监控服务器进程、文件、文件系统、网络状态(HTTP/SMTP等协议)、远程主机、服务器资源变化等等。…...

Python 时间戳转时间
一、 10 位 时间戳 ,秒级 转换为时间 方式一: import timetimeStamp 1717149862 timeArray time.localtime(timeStamp) timeStr time.strftime("%Y-%m-%d %H:%M:%S", timeArray) print(timeStr) 输出: 2024-05-31 18:04:22方式二…...

【网络层】IP地址基础 与 子网掩码
文章目录 IP地址基础IP地址概念IP地址分类公网地址和私网地址 子网掩码子网掩码作用默认子网掩码网络地址、主机地址、广播地址 IP地址基础 IP地址概念 IP地址:IP Address 在网络中,通信节点都需要有一个IP地址 IP地址以点分十进制表示,有…...

Cholesky分解(A=L * L^T)
Cholesky分解是一种用于解线性方程组和计算矩阵平方根的算法,特别适用于对称正定矩阵。这种方法相比于其他解法(如高斯消元法)在数值稳定性上通常有更好的表现,并且能够有效地利用矩阵的对称性和正定性。下面简要介绍如何使用Chol…...

2024大模型新应用井喷即将到来,算力问题如何解决?
#大模型新应用 #NLP #智合同 随着人工智能技术的加速演进,AI大模型已成为全球科技竞争的新高地、未来产业的新赛道、经济发展的新引擎。大模型的快速发展,特别是在自然语言处理(NLP)和计算机视觉(CV)等…...

SpringBoot 七牛云 OSS 私有模式 获取访问链接
目录 一、问题引出 二、在SpringBoot中获取私有访问路径的操作 一、问题引出 由于七牛云OSS的公有模式存在被盗刷的风险,可能导致服务器额外的费用,于是我选择私有模式进行操作。私有模式的访问路径是一个问题,因为需要对应着token和e这两…...

python-模块-网络编程-多任务
一、模块 1-1 Python 自带模块 Json模块 处理json数据 {"key":"value"} json不是字典 本质是一个有引号的字符串数据 json注意点 {} 中的数据是字符串引号必须是双引号 使用json模块可以实现将json转为字典,使用字典的方法操作数据 。 或者将…...

火狐浏览器网页翻译
火狐浏览器网页翻译 火狐浏览器的翻译功能并不支持中文,无法将中文翻译成其他语言,也不支持将其他语言翻译成中文。如果需要翻译英文网页,可以安装翻译插件来帮助解决这个问题。 安装翻译插件的步骤如下: 打开火狐浏览器ÿ…...

R语言数据分析案例以及要点和难点
该案例将涵盖数据读取、数据清洗、探索性数据分析(EDA)、数据可视化和简单的统计分析等多个方面。本案例将以一家零售商店的销售数据为例,使用R语言进行数据分析。 1. 数据准备 假设我们有一个名为sales_data.csv的CSV文件,其中包含了零售商店的销售数据。该文件包含以下列…...

【STL源码剖析-空间配置器】stack、queue简单实现
举头天外望 无我这般人 目录 stack 的概述 stack 的实现 queue 的概述 queue 的实现 契子✨ 我们之前学过了 vector、list 这些 STL 的(容器) 而我们今天将要学习空间配置器 -- stack、queue,那什么是空间配置器呢? 简单来讲就是…...

VR导航的实现原理、技术优势和应用场景
VR导航通过虚拟现实技术提供沉浸式环境,结合室内定位技术实现精准导航。目前,VR导航已在多个领域展现出其独特的价值和潜力,预示着智能导航系统的未来发展。 一、实现原理 VR导航技术依托于虚拟现实(VR)和室内定位系统。VR技术利用计算机模…...

淘宝镜像的https证书过期
错误原因: 淘宝镜像过期 早在 2021 年,淘宝就发文称,npm 淘宝镜像已经从 http://registry.npm.taobao.org 切换到了 http://registry.npmmirror.com。旧域名也将于 2022 年 5 月 31 日停止服务(直到 HTTPS 证书到期才真正不能用了…...

VSCODE 常用快捷键
快捷按键 注释 CTRL /CTRL KSHIFT ALT A取消注释 CTRL /CTRL KSHIFT ALT A搜索文件 Ctrl P移动到某一行 Ctrl g打开一个新窗口 Ctrl Shift N关闭窗口 Ctrl Shift W新建文件 Ctrl N文件间切换 Ctrl Tab全部文件搜索 Ctrl Shift F全屏 F11 打开文件出现中文乱码 文件右下角…...

医院该如何应对网络安全?
在线医生咨询受到很多人的关注,互联网医疗行业的未来发展空间巨大,但随着医院信息化建设高速发展 医院积累了大量的患者基本信息、化验结果、电子处方、生产数据和运营信息等数据 这些数据涉及公民隐私、医院运作和发展等多因素,医疗行业办…...

【qt】多窗口开发
多窗口开发 一.应用场景二.嵌入的窗口1.设计Widget窗口2.创建窗口3.添加窗口4.总代码 三.独立的窗口1.创建窗口2.显示窗口 四.总结 一.应用场景 多窗口,顾名思义,有多个窗口可以供我们进行操作! 截个小图,你应该就知道了 OK,话不多说,直接开干,先来设计我们的主窗口 需要蔬菜…...

iOS Hittest 机制和实际应用
Hittest 机制原理 hitTest的原理就是,当我们点击的时候,会触发 window的 hittest方法,在该方法中会首先使用point inside方法判断 点击的地方是否在window范围内,如果在的话,就倒序遍历姿子视图,然后将poi…...

C# 工厂模式学习
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的接口,而不是通过具体类来实例化对象。工厂模式可以将对象的创建过程封装起来,使代码更具有灵活性和可扩展性。 工厂模式有几种常见的实现方式&…...

AI生成微信职业头像
加油,新时代打工人! 真别说,还挺好看的 https://chatglm.cn/main/alltoolsdetail...

遥感图像的深度学习的任务类型
在遥感图像的深度学习任务中,利用深度学习技术处理和分析遥感图像已经成为一个重要的研究方向。遥感图像来自卫星、无人机等设备,包含了丰富的地球表面信息。以下是遥感图像深度学习中的主要任务类型: 1. 图像分类(Image Classif…...

162.二叉树:填充每个节点的下一个右侧节点指针(力扣)
代码解决 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL), right(NULL), next(NULL) {}Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}Node(int _val, Node* _left, Node* _…...

NLP(20)--知识图谱+实体抽取
前言 仅记录学习过程,有问题欢迎讨论 基于LLM的垂直领域问答方案: 特点:不是通用语料;准确度要求高,召回率可以低(转人工);拓展性和可控性(改变特定内容的回答…...

【mysql数据库】mycat中间件
MyCat 简介 Mycat 是数据库 中间件 。 1、 数据库中间件 中间件 是一类连接软件组件和应用的计算机软件, 以便于软件各部件之间的沟通 。 例子 Tomcat web 中间件 。 数据库 中间件 连接 java 应用程序和数据库 2、 为什么要用 Mycat ① Java 与数据库紧耦合 …...

满帮集团 Eureka 和 ZooKeeper 的上云实践
作者:胡安祥 满帮集团,作为“互联网物流”的平台型企业,一端承接托运人运货需求,另一端对接货车司机,提升货运物流效率。2021 年美股上市,成为数字货运平台上市第一股。根据公司年报,2021 年&a…...