• C++数位动态规划算法:统计整数数目


    作者推荐

    视频算法专题

    本文涉及知识点

    动态规划汇总

    题目

    给你两个数字字符串 num1 和 num2 ,以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件,我们称它是一个好整数:
    num1 <= x <= num2
    min_sum <= digit_sum(x) <= max_sum.
    请你返回好整数的数目。答案可能很大,请返回答案对 109 + 7 取余后的结果。
    注意,digit_sum(x) 表示 x 各位数字之和。
    示例 1:
    输入:num1 = “1”, num2 = “12”, min_num = 1, max_num = 8
    输出:11
    解释:总共有 11 个整数的数位和在 1 到 8 之间,分别是 1,2,3,4,5,6,7,8,10,11 和 12 。所以我们返回 11 。
    示例 2:
    输入:num1 = “1”, num2 = “5”, min_num = 1, max_num = 5
    输出:5
    解释:数位和在 1 到 5 之间的 5 个整数分别为 1,2,3,4 和 5 。所以我们返回 5 。
    提示:
    1 <= num1 <= num2 <= 1022
    1 <= min_sum <= max_sum <= 400

    分析

    a. 先考虑数位相等

    DoSameLen函数处理数位相等。用动态规划,假定已经处理了i位,正在处理第i位。对于某种状态,我们只关心:一,位数和,取值范围[0, max_sum],共max_sum+1种,大于max_sum的状态抛弃。二,上下界。非边界、上边界、下边界、上下界。如:num1=”121”,num2=”142”则”1”是上下界,"12"是下界,"13"是非边界、"14"是上边界。如果上下界,则当前位只能取值[num1[i],nums2[i]]。如果上边界,则只能取值[0,nums2[i]]。如果是下边界,则能取[nums1[i],9]。如果是非边界,则可以任意取值。

    前一轮状态当前条件新状态
    上下界同时等于num1[i]和num2[i]上下界
    上下界等于num1[i]下界
    上下界等于num2[i]上界
    上下界num1[i]和num2[i]之间非边界
    上界等于num2[i]上界
    上界小于num2[i]非边界
    下界等于num1[i]下界
    下界大于num1[i]非边界
    非边界0到9非边界

    b. 优化掉上下界

    上下界一定出现再最前面,且数量为1。当i为0时,有新的上下界,那说明num1[0]和num2[i],只能选择num1[i]。当i大于0是,有新的上下界,那么num1[i]等于num2[i],由于(i-1)也是上下界,所有num1和num2的前i+1个元素相同。由于第i为只能num1[i],所以数量不变。
    假定num1和num2的前i位完全相同,则前i位只有一个合法数。
    这意味这num1和num2扔掉相同的前者,边界状态的数量不变。比如:
    num1 = “112”和num2 = “115”, 下界:115,非边界:113和114 上界:115。
    num1 = “12”和num2 = “15”, 下界:15,非边界:13和14 上界:15。
    num1 = “2”和num2 = “5”, 下界:5,非边界:3和4 上界:5。

    c. 考虑数位不等

    假定num1的长度为l1,num2的长度为l2,则分别:
    Do(num1,string(l1,’9’)
    for(int i = l1+1; i < l2 ;i++)
    {
    Do(‘1’+string(i-1,’0’),string(i,’9’);
    }
    Do(‘1’+string(i2-1,’0’),num2)
    比如:
    nums1 = “1”,nums2=”19”
    Do(“1”,”9”)
    Do(“10”,”19”)
    再如:
    nums1 = “1”,nums2=”123”
    Do(“1”,”9”)
    Do(“10,”99”)
    Do(“100,”123”)
    注意:
    l1等于l2时要分别处理。

    核心代码

    class Solution {
    public:
    int count(string num1, string num2, int min_sum, int max_sum) {
    m_min_sum = min_sum;
    m_max_sum = max_sum;
    const int l1 = num1.length();
    const int l2 = num2.length();
    if (l1 == l2)
    {
    return DoSameLen(num1, num2).ToInt();
    }
    C1097Int biRet;
    biRet += DoSameLen(num1, string(l1, ‘9’));
    for (int i = l1 + 1; i < l2; i++)
    {
    biRet += DoSameLen(“1” + string(i - 1, ‘0’), string(i,‘9’));
    }
    biRet += DoSameLen(“1” + string(l2-1,‘0’), num2);
    return biRet.ToInt();
    }
    C1097Int<> DoSameLen(string num1, string num2)
    {
    int n = num1.size();//只处理相等位数
    int i = 0;
    int sum = 0;
    for (; (i < n) && (num1[i] == num2[i]); i++)
    {
    sum += num1[i] - ‘0’;
    }
    if (i >= n)
    {
    return ((sum >= m_min_sum)&&( sum <= m_max_sum)) ? 1 : 0;
    }
    //注[1]
    vector>> pre(3, vector>(m_max_sum+1));
    Set(pre, 1, sum + num1[i]-‘0’, 1);
    for (int j = num1[i] + 1; j < num2[i]; j++)
    {
    Set(pre, 0, sum +j - ‘0’, 1);
    }
    Set(pre, 2, sum + num2[i] - ‘0’, 1);
    for (i++; i < n; i++)
    {
    vector>> dp(3, vector>(m_max_sum + 1));
    for (int preSum = 0; preSum <= m_max_sum; preSum++)
    {
    //处理下界
    Set(dp, 1, preSum + num1[i] - ‘0’, pre[1][preSum]);
    for (int j = num1[i] + 1; j <= ‘9’; j++)
    {
    Set(dp, 0, preSum + j - ‘0’, pre[1][preSum]);
    }
    //处理无边界
    for (int j = ‘0’; j <= ‘9’; j++)
    {
    Set(dp, 0, preSum + j - ‘0’, pre[0][preSum]);
    }
    //处理上界
    for (int j = ‘0’; j < num2[i]; j++)
    {
    Set(dp, 0, preSum + j - ‘0’, pre[2][preSum]);
    }
    Set(dp,2, preSum + num2[i] - ‘0’, pre[2][preSum]);
    }
    pre.swap(dp);
    }
    return Cal(pre);
    }
    void Set(vector>>& dp, int status, int sum,C1097Int<> iAddNum)
    {
    if (sum > m_max_sum)
    {
    return;
    }
    dp[status][sum] += iAddNum;
    }
    C1097Int<> Cal(const vector>>& pre)
    {
    C1097Int<> biRet;
    for (int i = 0; i < 3; i++)
    {
    for (int j = m_min_sum; j <= m_max_sum; j++)
    {
    biRet += pre[i][j];
    }
    }
    return biRet;
    }
    int m_min_sum, m_max_sum;
    };

    测试代码

    int main()
    {
    int res = 0;
    res = Solution().count(“2”, “5”, 0, 100);
    assert(res == 4);
    res = Solution().count(“12”, “15”, 0, 100);
    assert(res == 4);
    res = Solution().count(“112”, “115”, 0, 100);
    assert(res == 4);
    res = Solution().count(“112”, “115”, 5, 100);
    assert(res == 3);
    res = Solution().count(“112”, “115”, 0, 6);
    assert(res == 3);
    res = Solution().count(“112”, “115”, 5, 6);
    assert(res == 2);
    res = Solution().count(“1”, “12”, 1, 8);
    assert(res == 11);

    CConsole::Out(res);
    
    • 1

    }
    在这里插入图片描述

    其它

    视频课程

    如果你觉得复杂,想从简单的算法开始,可以学习我的视频课程。
    https://edu.csdn.net/course/detail/38771
    我的其它课程
    [https://edu.csdn.net/lecturer/6176]

    (https://edu.csdn.net/lecturer/6176)

    测试环境

    win7 VS2019 C++17

    相关下载

    算法精讲《闻缺陷则喜算法册》doc版
    https://download.csdn.net/download/he_zhidan/88348653

    在这里插入图片描述

  • 相关阅读:
    中英双语多语言外贸企业网站源码系统 - HanCMS - 安装部署教程
    源码安装MAVROS
    Java之Map集合
    报错:To see the full stack trace of the errors, re-run Maven with the -e switch.
    【基于FreeRTOS的STM32F103系统】内存管理及任务调度
    JAVA:实现Blowfish区块加密算法(附完整源码)
    2022/09/02 day02:连接远程仓库,推送、克隆
    1.2 ElasticSearch核心术语
    Visio 安装与操作小结
    Pipeline流水线设计的最佳实践
  • 原文地址:https://blog.csdn.net/he_zhidan/article/details/133816628