#sWDPybttg050302. 1586:【 例 2】数字游戏

1586:【 例 2】数字游戏

好的,我将这道题整理为清晰的题面格式,并补充样例解释、数据范围与时空限制:


题目描述

科协里最近很流行数字游戏。某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如 123123446446。现在大家决定玩一个游戏,指定一个整数闭区间 [a,b][a,b],问这个区间内有多少个不降数。


输入格式

有多组测试数据。每组只含两个整数 a,ba, b,意义如题目描述。

输入以文件结束(EOF)为终止。


输出格式

对于每组测试数据,输出一行一个整数,表示 [a,b][a,b] 之间不降数的个数。


样例

样例输入

1 9
1 19

样例输出

9
18

样例解释

第一组:[1,9][1,9],所有一位数 1,2,,91,2,\dots,9 都是不降数(因为没有更低的位比较),共 99 个。

第二组:[1,19][1,19]

  • 一位数:191\sim 9,共 99 个;
  • 两位数不降数:11,12,13,14,15,16,17,18,19,22,23,,9911,12,13,14,15,16,17,18,19,22,23,\dots,99
    1919 以内只有:11,12,13,14,15,16,17,18,1911,12,13,14,15,16,17,18,19,共 99 个。

所以总不降数 9+9=189+9=18 个。

注意 1010 不是不降数(1>01>0),所以不在内。


数据范围

对于全部数据,1ab23111 \le a \le b \le 2^{31}-1


时空限制

  • 时间:1000 ms1000 \text{ ms}
  • 内存:524288 KB524288 \text{ KB}

提示
此题为 数位 DP 经典题。

状态设计:dp[pos][last]dp[pos][last] 表示当前处理到第 pospos 位(从高位到低位),上一位数字是 lastlast 时,满足不降数条件的数的个数。

转移:当前位 curcur 必须 last\ge last,枚举 curcurlastlast99

但这里需要注意,当 lastlast 初值为 00(表示前面没有数字),则当前位可以从 11 开始(因为不能有前导零?实际上对于位数不足的情况,前导零不影响大小,但在比较时需要特殊处理:如果前面都是前导零,则当前位可以从 00 开始,但一旦开始填非零数字,就不能再填比它小的数字?实际上更简单的办法是:允许前导零存在,但在最终数中前导零不视为数字,所以当 last=0 且当前位填 0 时,last 仍为 0,继续传递)。

因此状态可设为 dp[pos][last][lead]dp[pos][last][lead],其中 leadlead 表示是否前面全是前导零。

简化做法:
由于不降数要求数字非降,我们可以直接枚举数字长度 lenlen,然后从 090\sim 9 中选 lenlen 个数字(可重复,但必须非降),这就是组合数问题。但因为有前导零的存在(即实际位数可能小于 lenlen),需要小心处理。

更通用的数位 DP 方法:

  • 将数字 nn 拆分成数位数组 digitsdigits
  • DFS(pos, last, limit, lead):
    • pos:当前处理到的位;
    • last:上一位填的数字(若无上一位则设为 0);
    • limit:是否受到 nn 的限制;
    • lead:是否前面全是 0。
  • 记忆化:dp[pos][last][lead],limit 不记忆(因为 limit 为 1 的状态只对应一种情况)。
  • 转移:枚举当前位数字 dd 从 (lead ? 0 : last) 到 (limit ? digits[pos] : 9),且必须满足 dlastd \ge last 或 lead 为真(即前面全 0 时可以任意开始)。

最终答案为:dfs(0, 0, true, true)。

复杂度:状态数 O(10×位数×2)O(10 \times \text{位数} \times 2),可以很快。

注意:数字 00 是否算不降数?00 是一位数,且满足不降,但题目区间从 11 开始,所以不影响。若区间包含 00,需单独考虑。