首页 > 其他分享 >数位 DP

数位 DP

时间:2023-06-14 10:36:26浏览次数:36  
标签:idx int mask limit str DP 数位

引入

数位是指把一个数字按照个、十、百、千、万等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 $0\sim 9$。

数位 DP 一般是用来解决一类特定问题,以 1012. Numbers With Repeated Digits (Hard) 为例,这一类问题的特征非常明显

  1. 要求统计满足一定条件的数的数量;
  2. 这些条件经过转化后可以使用“数位”的思想去理解和判断;
  3. 输入会提供一个数字区间(有时候只提供上界)来作为统计的限制;
  4. 上界很大(例如 $10^{22}$),暴力枚举会超时。

思路

1012. Numbers With Repeated Digits (Hard),思路如下:

首先,正难则反,我们可以考虑 $[1, n]$ 范围内无重复数字的正整数的个数,记为 $res$,然后最终结果就是 $n - res + 1$(至于为什么还要再加上 $1$,后面会说明)。

因此问题转化为求 $[1, n]$ 范围内无重复数字的正整数的个数,符合上述的数位 DP 的特征,我们可以从记忆化搜索的角度去考虑,首先将 $n$ 转化为对应的字符串,对字符串的每一位,枚举每一位可能的数,如果最后组成的数字满足 $num < n$,那么 $res += 1$,这里很容易想到 dfs(string &str, int idx, int mask),$mask$ 以二进制的形式表示 $0\sim 9$ 范围内的数是否被选择过;

但是仅仅是这样,我们不能方便的判断当前组成的数字是否满足 $num < n$,因此,我们需要一个额外的参数 $is_limit$。例如对数字 $n = 12345$,如果前面已经选择的数字为 $123$,那么对本次枚举,$is_limit$ 为 $true$,即数字只能选择 $0\sim4$,又因为 $1,\ 2,\ 3$ 已经选择了,$mask = 14$,因此只有 $0,\ 4$ 可以选,事实上,我们可以发现,在递归的过程中,当且仅当当前 $is_limit$ 为 $true$,且当前选择的数字与对应数位上的数字相等时,更深一层递归的 $is_limit$ 仍为 $true$,至此,递归函数为 dfs(string &str, int idx, int mask, bool is_limit)

此外,由于我们递归的终止条件应该是 $idx \geq str.size()$,这样就只能统计出位数与 $n$ 相同的情况,而位数小于 $n$ 的就被忽略了,这是我们不能接受的,因此我们还需要一个参数 $filled$ 来标记之前的位有没有被填充,假设 $filled$ 为 $false$,说明之前没有被填充,那么有两种选择

  • 当前位也不填充,res += dfs(str, idx + 1, false, mask, filled, cach)
  • 如果要填充,那么必须从 $1$ 开始填充(因为不能有前导 $0$),res += dfs(str, idx + 1, false, mask, true, cach)

模板

根据以上分析,我们可以总结出一个数位 DP 适用的模板,如下:

int dfs(string &str, int idx, bool is_limit, int mask, bool filled, vector<vector<vector<vector<int>>>> &cach) {
    if (idx == str.size()) {
        return 1; // 说明找到了一个合理的选择
    }
    if (cach[idx][is_limit][mask][filled] != -1) {
        return cach[idx][is_limit][mask][filled];
    }
    int limit = is_limit ? str[idx] - '0' : 9;
    int res = 0;
    if (!filled) {
        // 前面的数位都没有填充
        res += dfs(str, idx + 1, false, mask, false, cach);
    }
    int left = filled ? 0 : 1;
    for (int i = left; i <= limit; ++i) {
        if (((1 << i) & mask) != 0) {
            // 说明 i 已经选择过了
            continue;
        }
        res += dfs(str, idx + 1, is_limit && (i == str[idx] - '0'), mask | (1 << i), true, cach);
    }
    cach[idx][is_limit][mask][filled] = res;
    return res;
}

在这个模板中,由于所有位都不填,也会被统计进结果中,因此 $res$ 比实际的 $res$ 多了 $1$,我们可以在最后计算的时候减去 $1$,也可以把边界条件的返回值修改为如下:

if (idx == str.size()) {
    return is_num; // is_num 为 true 说明找到了一个合法的数字,为 false 说明所有位都不填
}

参考

OI-Wiki:数位 DP

0x3f:数位 DP 通用模板

标签:idx,int,mask,limit,str,DP,数位
From: https://www.cnblogs.com/zwyyy456/p/17479463.html

相关文章

  • 【大数据】大数据 Hadoop 管理工具 Apache Ambari(HDP)
    目录一、概述二、Ambari与HDP关系三、Ambari与Clouderamanager的对比1)开源性2)支持的发行版3)用户界面4)功能和扩展性5)社区支持和生态系统四、ApacheAmbari术语五、ApacheAmbari核心组件介绍六、ApacheAmbari架构1)Ambari-agent内部架构2)Ambari-server内部架构3)Ambari......
  • 数位 DP
    引入数位是指把一个数字按照个、十、百、千、万等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是$0\sim9$。数位DP一般是用来解决一类特定问题,以1012.NumbersWithRepeatedDigits(Hard)为例,这一类问题的特征非常明显要求统计满足一定......
  • docker-compose搭建wordpress
    前言我们都知道,docker是一个运行容器的软件。同时它也配置了一个运行一组容器的软件,叫做docker-compose。当我们使用非常小规模容器化应用的时候,我们可以使用此docker-compose去做。 docker-compose的介绍根据官网的描述,docker-compose是一个能够管理多容器的工具,可以使用yam......
  • 调整数组顺序使奇数位于偶数前面
    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。示例:输入:nums= [1,2,3,4]输出:[1,3,2,4]注:[3,1,2,4]也是正确的答案之一。来源:力扣(LeetCode)链接:https://leetcode.cn/problems/diao-zheng-shu-zu-shun-xu-s......
  • eDP1.4a/DP1.4 转 GMSL2 方案
    GMSL2点屏及老化,应用于车载中控及仪表屏 ......
  • Codeforces Round #287 (Div. 2)-D. The Maths Lecture(数位dp)
    原题链接D.TheMathsLecturetimelimitpertestmemorylimitpertestinputoutputAmrdoesn'tlikeMathsashefindsitreallyboring,soheusuallysleepsinMathsl......
  • Linux RDP 会话中无法打开VSCode 解决办法
    githubissue:VSCode"andstill"won'topeninaLinuxxrdpsessionWorkaround-LinuxRDP会话中无法打开VSCode解决办法ThistimearoundIresolvedtheissuebynarrowingthefollowingHackintwosteps:Copythesystemfile'libxcb.so.1.1.0'......
  • papamelon 344. 奶牛展览 Cow Exhibition(挑战程序设计竞赛) dp
    地址https://www.papamelon.com/problem/344贝西有权选择让哪些奶牛参加展览。由于负的智商或情商会造成负面效果,所以贝西不希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛的智商与情商之和越大越好,请帮助贝西求出这个最大值。输入第一行:......
  • CodeForces 4B Before an Exam(DP)
    思路:令dp[i][j]为做了i天用了j时间,然后随便转移一下就可以了#include<cstdio>#include<queue>#include<cstring>#include<iostream>#include<cstdlib>#include<algorithm>#include<vector>#include<map>#include<string>#......
  • POJ1837 DP
    POJ1837DP题题目一开始看了N久…意思大概是有一个天平,左边臂长是-15到0,右边臂长是0到15,给你c个挂钩,g个砝码,每一个砝码重量都在1到25,问将所有砝码挂到天平上并使之平衡的方案有多少个。要使之平衡由物理知识可知力矩=0,左边重量X左边臂长+右边重量X右边臂长=0,故状态一共有25*15......