当前位置: 首页 > news >正文

【数位dp】3704. 统计和为 N 的无零数对|2419

本文涉及知识点

C++动态规划 数位dp

3704. 统计和为 N 的无零数对

一个 无零 整数是一个十进制表示中 不包含数字 0 的 正 整数。
给定一个整数 n,计算满足以下条件的数对 (a, b) 的数量:
a 和 b 都是 无零 整数。
a + b = n
返回一个整数,表示此类数对的数量。
示例 1:
输入: n = 2
输出: 1
解释:
唯一的数对是 (1, 1)。
示例 2:
输入: n = 3
输出: 2
解释:
数对有 (1, 2) 和 (2, 1)。
示例 3:
输入: n = 11
输出: 8
解释:
数对有 (2, 9)、(3, 8)、(4, 7)、(5, 6)、(6, 5)、(7, 4)、(8, 3) 和 (9, 2)。请注意,(1, 10) 和 (10, 1) 不满足条件,因为 10 在其十进制表示中包含 0。
提示:
2<=n<=10152 <= n <= 10^{15}2<=n<=1015

数位dp

性质一:由于任意数位都不能是0,故所有所有位的范围都是’1’到’9’。为了避免考虑不同的位数,容许所有位为0,非前导0,在枚举时抛弃。
性质二0≤a≤n,a+b=n,→0≤b≤n0 \le a \le n,a+b=n, \rightarrow 0 \le b \le n0an,a+b=n,0bn,枚举0≤n0 \le n0n的a的数量,a无0且n-b无零,不包括前导0。b一定≥0\ge 00。需要扣除(0,n)和(n,0)。如果n的字符串形式包括非前导0,则无需扣除。避免重复扣除。

封装类

dp[n] 表示当前字符串长度为n的前缀不是上限的方案数,upDp[n]表示当前字符串长度为n的前缀是上限的方案数。
故我们只需要考虑自定义状态m。m&4 表示后N-n位是否有进位,m&2表示a是否有前导0,m&1表示b是否有前导0。如果b有前导零,则b的当前位必须是0。

实现

用m枚举下一位的状态,用carrycur 枚举当前位(第n位)是否进位。carrynext 表示下一位(第n+1位)是否进位。a表示a的当前位,b表示b的当前位。则(carrycur?10:0)+strN[n]−′0′=a+b+carrynext(carrycur?10:0)+strN[n]-'0'=a+b+carrynext(carrycur?10:0)+strN[n]0=a+b+carrynext
如果 b < 0 忽略。
如果(m&2)且a>0,忽略。
如果(m&1)且b>0,忽略。
当前位自定义状态m1 = 8carrycur+4(0= =a)+2*(0= =b)
合法的初始状态:carry 非0。
合法的终止状态:dp[0]=upDp[0]=1,其它0。

代码

核心代码

template<class ELE, class ResultType>
class IUpperDPCall
{
public:virtual void OnEnum(int n,vector<ResultType>& dp, const vector<ResultType>& vNext, ELE curValue, int iCustomStatusCount) = 0;virtual void OnInitEnd(vector<ResultType>& dp, vector<ResultType>& upDp) = 0;
};template<class ELE, class ResultType>
class CUperrDP
{
public:CUperrDP(int iCustomStatusCount, IUpperDPCall<ELE, ResultType>& call, ELE minEle, ELE maxEle):m_iCustomStatusCount(iCustomStatusCount),m_call(call),m_minEle(minEle),m_maxEle(maxEle){}void Init(const ELE* pHigh, int iEleCount){m_vDP.assign(iEleCount + 1, vector<ResultType>(m_iCustomStatusCount));m_vDpUpper = m_vDP;m_call.OnInitEnd(m_vDP.back(), m_vDpUpper.back());//预处理增加的一位for (int i = iEleCount - 1;i > 0;i--) {m_call.OnEnum(i,m_vDpUpper[i], m_vDpUpper[i + 1], pHigh[i],m_iCustomStatusCount);for (auto j = m_minEle; j < pHigh[i];j++) {m_call.OnEnum(i,m_vDpUpper[i], m_vDP[i + 1], j, m_iCustomStatusCount);}for (auto j = m_minEle; j <= m_maxEle;j++) {m_call.OnEnum(i,m_vDP[i], m_vDP[i + 1], j, m_iCustomStatusCount);}}m_call.OnEnum(0,m_vDpUpper[0], m_vDpUpper[1], pHigh[0], m_iCustomStatusCount);for (auto j = m_minEle; j < pHigh[0];j++) {m_call.OnEnum(0,m_vDP[0], m_vDP[1], j, m_iCustomStatusCount);}}ResultType Sum(int iMinCustomStatu, int iMaxCustomStatu) {ResultType ret = 0;for (int i = iMinCustomStatu; i <= iMaxCustomStatu;i++) {ret += m_vDP[0][i] + m_vDpUpper[0][i];}return ret;}ResultType Sum() {return Sum(0, m_iCustomStatusCount - 1);}vector<vector<ResultType>> m_vDP, m_vDpUpper;const ELE m_minEle, m_maxEle;
protected:const int m_iCustomStatusCount;IUpperDPCall<ELE, ResultType>& m_call;
};template<class ELE, class ResultType>class CCall :public IUpperDPCall<ELE, ResultType>{public:CCall(const string& str):m_str(str){}	virtual void OnInitEnd(vector<ResultType>& dp, vector<ResultType>& upDp) {dp[0] = upDp[0] = 1;}virtual void OnEnum(int n, vector<ResultType>& dp, const vector<ResultType>& vNext, ELE curValue, int iCustomStatusCount){int a = curValue - '0';for (int carryCur = 0;carryCur < 2;carryCur++) {for (int m = 0;m < 8;m++) {const bool carryNext = m & 4;int b = (carryCur ? 10 : 0) + m_str[n] - '0' - a - carryNext;if ((b < 0)||(b>9)) { continue; }if ((m & 2) && a) { continue; }if ((m & 1) && b) { continue; }const int m1 = 4 * carryCur + 2 * (0 == a) + (0 == b);dp[m1] += vNext[m];}}}string m_str;};class Solution {public:long long countNoZeroPairs(long long n) {string s = to_string(n);CCall<char, long long> call(s);CUperrDP<char, long long> dp(8, call, '0', '9');dp.Init(s.c_str(), s.length());long long ans = dp.Sum(0, 3) ;if (-1 == s.find("0")) { ans -= 2; }return ans ;}};

单元测试

TEST_METHOD(TestMethod11){auto res = Solution().countNoZeroPairs(2);AssertEx(1LL, res);}TEST_METHOD(TestMethod12){auto res = Solution().countNoZeroPairs(3);AssertEx(2LL, res);}TEST_METHOD(TestMethod13){auto res = Solution().countNoZeroPairs(11);AssertEx(8LL, res);}TEST_METHOD(TestMethod14){auto res = Solution().countNoZeroPairs(10);AssertEx(9LL, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

http://www.dtcms.com/a/483888.html

相关文章:

  • 快速学制作网站株洲seo优化公司
  • 【Datawhale组队学习】math-for-ai TASK01
  • 个人网站主页模板wp用户前端化专业版wordpress插件[中英双语]
  • 看设计比较好的网站在线装修设计平台
  • 网站建设目标分析学校网站建设工作方案
  • 【Linux】Linux驱动开发与BSP开发:嵌入式系统的两大基石
  • 郑州机械网站建设张家港网站制作服务
  • 动叫建个网站刷排名郑州高端做网站
  • 波矢 行波 和 相速度推导
  • LeetCode算法日记 - Day 72: 下降路径最小和、珠宝的最高价值
  • 天津专业智能建站wordpress 转换app
  • 周口市住房和城乡建设局门户网站wordpress邮件服务器怎么设置
  • 新版 网站在建设中...工信部 网站 备案
  • Flutter---showCupertinoDialog
  • 万州做网站seo优化一般包括哪些
  • 网站建设优化兼职在家漯河网站建设费用
  • 学校网站的英文一个人如何注册公司
  • 哪些网站用php余姚网站建设设计服务
  • PS成长之路⑧:如何判断生成何种需求类型满足场景需求
  • 如何把资料上传到网站威海城乡建设局网站
  • 专业制作网站 上海模版做网站多少钱
  • 快速掌握【Redis】Set:从基础命令到内部编码
  • [Linux系统编程——Lesson13.自定义shell(讲解原理)]
  • 微信开放平台官方网站家电电商平台排名
  • 贵州建设厅考试网站二建成绩自己动手建设公司门户网站
  • 湛江网站建设方案托管个人网页设计html加js代码
  • 崇左网站建设网站如何做绿标
  • 湛江制作网站公司互联网站的建设维护营销
  • 陪跑教学大纲:PowerBI QuickBI FineBI 数据运营 面试 简历修改等
  • 做海报有什么好的网站推荐网络营销网站建设诊断报告