【动态规划】【 数位dp】2827. 范围中美丽整数的数目

简介: 【动态规划】【 数位dp】2827. 范围中美丽整数的数目

本文涉及知识点

数位dp

动态规划汇总

LeetCode2827. 范围中美丽整数的数目

给你正整数 low ,high 和 k 。

如果一个数满足以下两个条件,那么它是 美丽的 :

偶数数位的数目与奇数数位的数目相同。

这个整数可以被 k 整除。

请你返回范围 [low, high] 中美丽整数的数目。

示例 1:

输入:low = 10, high = 20, k = 3

输出:2

解释:给定范围中有 2 个美丽数字:[12,18]

  • 12 是美丽整数,因为它有 1 个奇数数位和 1 个偶数数位,而且可以被 k = 3 整除。
  • 18 是美丽整数,因为它有 1 个奇数数位和 1 个偶数数位,而且可以被 k = 3 整除。
    以下是一些不是美丽整数的例子:
  • 16 不是美丽整数,因为它不能被 k = 3 整除。
  • 15 不是美丽整数,因为它的奇数数位和偶数数位的数目不相等。
    给定范围内总共有 2 个美丽整数。
    示例 2:
    输入:low = 1, high = 10, k = 1
    输出:1
    解释:给定范围中有 1 个美丽数字:[10]
  • 10 是美丽整数,因为它有 1 个奇数数位和 1 个偶数数位,而且可以被 k = 1 整除。
    给定范围内总共有 1 个美丽整数。
    示例 3:
    输入:low = 5, high = 5, k = 2
    输出:0
    解释:给定范围中有 0 个美丽数字。
  • 5 不是美丽整数,因为它的奇数数位和偶数数位的数目不相等。
    提示:
    0 < low <= high <= 109
    0 < k <= 20

数位dp

直接使用封装类,枚举类型char,最小值’0’,最大值’9’,结果类型:int。

状态数量:400。

i1 = 奇数数数量-偶数位数量+10,取值范围∈ \in[1,19]

i2 = 当前数字%k

状态压缩:20*i1+i2

代码

核心代码

template<class ELE, class ResultType, ELE minEle, ELE maxEle>
class CLowUperr
{
public:
  CLowUperr(int iCustomStatusCount) :m_iCustomStatusCount(iCustomStatusCount)
  {
  }
  void Init(const ELE* pLower, const ELE* pHigh, int iNum)
  {
    m_vPre.assign(4, vector<ResultType>(m_iCustomStatusCount));
    if (iNum <= 0)
    {
      return;
    }
    InitPre(pLower, pHigh);
    iNum--;
    while (iNum--)
    {
      pLower++;
      pHigh++;
      vector<vector<ResultType>> dp(4, vector<ResultType>(m_iCustomStatusCount));
      OnInitDP(dp);
      //处理非边界
      for (auto tmp = minEle; tmp <= maxEle; tmp++)
      {
        OnEnumOtherBit(dp[0], m_vPre[0], tmp);
      }
      //处理下边界
      OnEnumOtherBit(dp[1], m_vPre[1], *pLower);
      for (auto tmp = *pLower + 1; tmp <= maxEle; tmp++)
      {
        OnEnumOtherBit(dp[0], m_vPre[1], tmp);
      }
      //处理上边界
      OnEnumOtherBit(dp[2], m_vPre[2], *pHigh);
      for (auto tmp = minEle; tmp < *pHigh; tmp++)
      {
        OnEnumOtherBit(dp[0], m_vPre[2], tmp);
      }
      //处理上下边界
      if (*pLower == *pHigh)
      {
        OnEnumOtherBit(dp[3], m_vPre[3], *pLower);
      }
      else
      {
        OnEnumOtherBit(dp[1], m_vPre[3], *pLower);
        for (auto tmp = *pLower + 1; tmp < *pHigh; tmp++)
        {
          OnEnumOtherBit(dp[0], m_vPre[3], tmp);
        }
        OnEnumOtherBit(dp[2], m_vPre[3], *pHigh);
      }
      m_vPre.swap(dp);
    }
  }
protected:
  const int m_iCustomStatusCount;
  void InitPre(const ELE* const pLower, const ELE* const pHigh)
  {
    for (ELE cur = *pLower; cur <= *pHigh; cur++)
    {
      int iStatus = 0;
      if (*pLower == cur)
      {
        iStatus = *pLower == *pHigh ? 3 : 1;
      }
      else if (*pHigh == cur)
      {
        iStatus = 2;
      }
      OnEnumFirstBit(m_vPre[iStatus], cur);
    }
  }
  virtual void OnEnumOtherBit(vector<ResultType>& dp, const vector<ResultType>& vPre, ELE curValue) = 0;
  virtual void OnEnumFirstBit(vector<ResultType>& vPre, const ELE curValue) = 0;
  virtual void OnInitDP(vector<vector<ResultType>>& dp)
  {
  }
  vector<vector<ResultType>> m_vPre;
};
class CMy : public CLowUperr<char, int, '0', '9'>
{
public:
  typedef  int ResultType;
  typedef  char ELE;
  CMy(int k) :CLowUperr<char, int, '0', '9'>(400), m_iK(k)
  {
  }
  virtual void OnEnumOtherBit(vector<ResultType>& dp, const vector<ResultType>& vPre, ELE curValue)
  {
    const int index = curValue - '0';
    for (int iPreMask = 0; iPreMask < m_iCustomStatusCount; iPreMask++)
    {
      const int preK = iPreMask % 20;
      const int pre01 = iPreMask / 20 ;
      const int k = (preK*10+index) % m_iK;
      int i01 = (index & 1) ? 1 : -1;
      if ((pre01 + i01 < 0) || (pre01 + i01 >= 20))
      {
        continue;
      }
      const int mask = Mask(pre01+i01-10, k);
      dp[mask] += vPre[iPreMask];
    }
  }
  virtual void OnEnumFirstBit(vector<ResultType>& vPre, const ELE curValue)
  {
    const int index = curValue - '0';
    const int k = index % m_iK;
    int i01 = (index & 1) ? 1 : -1;
    const int mask = Mask(i01,k);
    vPre[mask]++;
  }
  int Result()const
  {
    int iRet = 0;
    for (int status = 0; status < 4; status++)
    {
      iRet += m_vPre[status][200];
    }
    return iRet;
  }
protected:
  int Mask(int i01, int k) {  return (i01 + 10) * 20 + k; }
  const int m_iK;
};
class Solution {
public:
  int numberOfBeautifulIntegers(int low, int high, int iK) {
    string strLow = std::to_string(low);
    string strHigh = std::to_string(high);
    int iRet = 0;
    const int len1 = strLow.length();
    const int len2 = strHigh.length();
    if (len1 == len2)
    {
      return Do(strLow, strHigh, iK);
    }
    iRet += Do(strLow, string(len1, '9'),iK);
    for (int i = len1 + 1; i < len2; i++)
    {
      iRet += Do("1" + string(i - 1, '0'), string(i, '9'), iK);
    }
    iRet += Do("1" + string(len2 - 1, '0'), strHigh, iK);
    return iRet;
  }
  int Do(const string strLow,const string& strHigh,int k)
  {
    CMy my(k);
    my.Init(strLow.data(), strHigh.data(), strLow.length());
    return my.Result();
  }
};

测试用例

template<class T, class T2>
void Assert(const T& t1, const T2& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  
  int low = 1, high = 10, k = 1;
  {
    low = 1, high = 10, k = 1;
    auto res = Solution().numberOfBeautifulIntegers(low, high, k);
    Assert(1, res);
  }
  {
    low = 5, high = 5, k = 2;
    auto res = Solution().numberOfBeautifulIntegers(low, high, k);
    Assert(0, res);
  }
  {
    low = 10, high = 20, k = 3;
    auto res = Solution().numberOfBeautifulIntegers(low, high, k);
    Assert(2, res);
  }
  
}


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

相关文章
|
4月前
|
算法 测试技术 C++
【数位dp】【动态规划】C++算法:233.数字 1 的个数
【数位dp】【动态规划】C++算法:233.数字 1 的个数
|
4月前
|
算法 测试技术
【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字
【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字
|
4月前
|
算法 测试技术 C++
【动态规划】【状态压缩】【C++算法】1815 得到新鲜甜甜圈的最多组数
【动态规划】【状态压缩】【C++算法】1815 得到新鲜甜甜圈的最多组数
|
4月前
windy数(数位dp)
windy数(数位dp)
26 0
|
11月前
|
算法 测试技术 C++
C++二分算法习题:判断是否是完全平方数[容易]和排列箱子[容易]
C++二分算法习题:判断是否是完全平方数[容易]和排列箱子[容易]
|
3月前
|
存储
【洛谷 P1255】数楼梯 题解(递归+记忆化搜索+高精度)
这是一个使用动态规划解决的“数楼梯”问题。给定楼梯数`N`,求不同上楼方式的数量。程序通过递归函数`f()`计算,其中`f(x) = f(x - 1) + f(x - 2)`,初始条件`f(1) = 1`,`f(2) = 2`。考虑到数据规模可能很大,使用了高精度加法运算。样例输入`4`,输出`5`。代码中定义了一个存储中间结果的向量`mem`,并提供了一个用于显示高精度数的`printv()`函数。
39 0
|
1月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
33 0
|
3月前
|
Java
贪心 -力扣860.柠檬水找零力扣2208.将数组和减半的最少操作次数力扣179.最大数力扣376.摆动序列
贪心 -力扣860.柠檬水找零力扣2208.将数组和减半的最少操作次数力扣179.最大数力扣376.摆动序列
|
4月前
|
算法 测试技术 C#
【数位dp】【数论】【动态规划】2999. 统计强大整数的数目
【数位dp】【数论】【动态规划】2999. 统计强大整数的数目
消失的数字,旋转数组(leetcode 一题多解)
消失的数字,旋转数组(leetcode 一题多解)