Number of Beautiful Integers in the Range
Number of Beautiful Integers in the Range
You are given positive integers low, high, and k.
A number is beautiful if it meets both of the following conditions:
- The count of even digits in the number is equal to the count of odd digits.
- The number is divisible by k.
Return the number of beautiful integers in the range [low, high].
Example 1:
Input: low = 10, high = 20, k = 3 Output: 2 Explanation: There are 2 beautiful integers in the given range: [12,18]. - 12 is beautiful because it contains 1 odd digit and 1 even digit, and is divisible by k = 3. - 18 is beautiful because it contains 1 odd digit and 1 even digit, and is divisible by k = 3. Additionally we can see that: - 16 is not beautiful because it is not divisible by k = 3. - 15 is not beautiful because it does not contain equal counts even and odd digits. It can be shown that there are only 2 beautiful integers in the given range.
Example 2:
Input: low = 1, high = 10, k = 1 Output: 1 Explanation: There is 1 beautiful integer in the given range: [10]. - 10 is beautiful because it contains 1 odd digit and 1 even digit, and is divisible by k = 1. It can be shown that there is only 1 beautiful integer in the given range.
Example 3:
Input: low = 5, high = 5, k = 2 Output: 0 Explanation: There are 0 beautiful integers in the given range. - 5 is not beautiful because it is not divisible by k = 2 and it does not contain equal even and odd digits.
Constraints:
- 0 < low <= high <= 109
- 0 < k <= 20
解题思路
一碰到数位dp就谔谔,昨晚硬是磨了一个多小时都没做出来,然后今天调了一早上才过了,被傻逼lc的评测机制气乐了。
数位dp我一直都用递推去做的,记忆化搜索一直没学会。当时想状态定义的时候就想了很久,现在的总结就是其实只用关注从最高位往低位递推时会用到哪些特定的数,然后把需要的状态定义出来就好了,比如说最基本的数位的个数,最高位是哪一个数,然后是其他特性比如这题中奇偶数位分别有多少,整个数模是多少。
定义状态表示所有数位大小为,最高位是,奇数数位有个,偶数数位有个,整个数模为的数的个数。很明显数位大小为的数可以转移到数位大小为的数,因此当前状态可以转移到的状态就有
然后递推的时候求的是中有多少个数满足条件,先把中的每一个数分离出来,然后从最高位开始往底位枚举。只有的数位大小为偶数时才能递推,因为要求奇偶数位相同,并且在递推时要保证没有前导(前导会印象奇偶数位的数目),最后再把那些含前导的数补上即可。递推时需要开变量分别记录前面已确认的数的奇数数位和偶数数位有多少,以及组成的数在十进制中模是多少。
最后因为lc的逆天评测机制,如果对于每个样例都跑dp预处理那么肯定会TLE,因此需要把预处理的结果记录到全局变量中,这样只用跑一次即可,不过需要把所有情况都跑一遍,预处理的计算量大概是。
AC代码如下:
1 vector<vector<vector<vector<vector<vector<int>>>>>> f; 2 3 class Solution { 4 public: 5 void init() { 6 f = vector<vector<vector<vector<vector<vector<int>>>>>>(21, vector<vector<vector<vector<vector<int>>>>>(11, vector<vector<vector<vector<int>>>>(10, vector<vector<vector<int>>>(6, vector<vector<int>>(6, vector<int>(20)))))); 7 for (int k = 1; k <= 20; k++) { 8 vector<int> p(10); 9 p[0] = 1 % k; 10 for (int i = 1; i < 10; i++) { 11 p[i] = p[i - 1] * 10 % k; 12 } 13 for (int i = 0; i <= 9; i++) { 14 if (i & 1) f[k][1][i][1][0][i % k] = 1; 15 else f[k][1][i][0][1][i % k] = 1; 16 } 17 for (int i = 1; i < 10; i++) { 18 for (int j = 0; j <= 9; j++) { 19 for (int u = 0; u <= 5; u++) { 20 for (int v = 0; v <= 5; v++) { 21 for (int w = 0; w < k; w++) { 22 for (int x = 0; x <= 9; x++) { 23 if (x % 2 && u + 1 <= 5) f[k][i + 1][x][u + 1][v][(x * p[i] + w) % k] += f[k][i][j][u][v][w]; 24 else if (x % 2 == 0 && v + 1 <= 5) f[k][i + 1][x][u][v + 1][(x * p[i] + w) % k] += f[k][i][j][u][v][w]; 25 } 26 } 27 } 28 } 29 } 30 } 31 } 32 } 33 34 int numberOfBeautifulIntegers(int low, int high, int k) { 35 if (f.empty()) init(); 36 vector<int> p(10); 37 p[0] = 1 % k; 38 for (int i = 1; i < 10; i++) { 39 p[i] = p[i - 1] * 10 % k; 40 } 41 function<int(int)> get = [&](int x) { 42 if (!x) return 0; 43 vector<int> q; 44 while (x) { 45 q.push_back(x % 10); 46 x /= 10; 47 } 48 int ret = 0, odd = 0, even = 0, s = 0; 49 if (~q.size() & 1) { 50 int t = q.size() >> 1; 51 for (int i = q.size() - 1; i >= 0; i--) { 52 for (int j = i == q.size() - 1; j < q[i]; j++) { 53 ret += f[k][i + 1][j][t - odd][t - even][(k - s) % k]; 54 } 55 if (q[i] & 1) odd++; 56 else even++; 57 if (odd > t || even > t) break; 58 s = (s + q[i] * p[i]) % k; 59 if (!i && odd == even && !s) ret++; 60 } 61 } 62 for (int i = 1; i < q.size(); i++) { 63 if (~i & 1) { 64 for (int j = 1; j <= 9; j++) { 65 ret += f[k][i][j][i >> 1][i >> 1][0]; 66 } 67 } 68 } 69 return ret; 70 }; 71 return get(high) - get(low - 1); 72 } 73 };
这题还可以用暴搜,可以发现只有数位大小为偶数的数才能满足奇数数位和偶数数位的数目相同,因此可以暴搜出来这些数,实际上大概有个左右。然后在暴搜的时候要保证搜到的数有序,对于和二分出可选数的区间,然后再逐个枚举是否模为即可。详细见参考资料,这里就不写了,比赛很少会这样做,一般都直接数位dp。
2023-08-27更新:
受F - Nim这题的启发,题解中用到了我没接触过的新的dp状态表示,这里把这种新的状态定义用到这题。之后再把atc这题的题解补上。
对进行数位dp,在中求出所有满足条件的数的数量。定义状态表示满足以下条件的整数(十进制)的数量:
- 的数位大小为,或者说。
- 若,则;否则。其中表示在十进制下的最低有效位数字构成的值,例如的最低有效位数字构成的值就是。
- 的个数位中有个数位是奇数,个数位是偶数。
- 。
- 若的最高数位上的数是,则;否则。
这里解释一下为什么要加一个表示最高位上的数是否为的状态。这是因为在统计时数不能有前导,因此加上这个状态以示区别。
对于当前确定的状态,我们可以转移到数位是的状态,只需枚举数位是的数的最高位是哪个数字即可(记作),状态转移方程就是
其中表示的是在十进制下从低位开始的第位上的数字。表示的是,如果,则结果为;如果,则结果为。
AC代码如下,时间复杂度为:
1 class Solution { 2 public: 3 int numberOfBeautifulIntegers(int low, int high, int k) { 4 function<int(int)> get = [&](int x) { 5 if (!x) return 0; 6 vector<int> p; 7 while (x) { 8 p.push_back(x % 10); 9 x /= 10; 10 } 11 int sz = p.size(); 12 vector<vector<vector<vector<vector<vector<long long>>>>>> f(sz + 1, vector<vector<vector<vector<vector<long long>>>>>(2, vector<vector<vector<vector<long long>>>>(sz / 2 + 2, vector<vector<vector<long long>>>(sz / 2 + 2, vector<vector<long long>>(k, vector<long long>(2)))))); 13 for (int i = 0; i <= 9; i++) { 14 if (i & 1) f[1][i <= p[0]][1][0][i % k][!!i]++; 15 else f[1][i <= p[0]][0][1][i % k][!!i]++; 16 } 17 vector<int> p10(sz); 18 p10[0] = 1; 19 for (int i = 1; i < sz; i++) { 20 p10[i] = (p10[i - 1] * 10) % k; 21 } 22 for (int i = 1; i < sz; i++) { 23 for (int j = 0; j <= 1; j++) { 24 for (int u = 0; u <= sz >> 1; u++) { 25 for (int v = 0; v <= sz >> 1; v++) { 26 for (int w = 0; w < k; w++) { 27 for (int x = 0; x <= 1; x++) { 28 for (int y = 0; y <= 9; y++) { 29 if (y & 1) f[i + 1][y < p[i] || y == p[i] && j][u + 1][v][(y * p10[i] + w) % k][!!y] += f[i][j][u][v][w][x]; 30 else f[i + 1][y < p[i] || y == p[i] && j][u][v + 1][(y * p10[i] + w) % k][!!y] += f[i][j][u][v][w][x]; 31 } 32 } 33 } 34 } 35 } 36 } 37 } 38 int ret = 0; 39 for (int i = 2; i <= sz; i += 2) { // 只考虑位数个数是偶数的数 40 ret += f[i][1][i >> 1][i >> 1][0][1]; // 在位数为i的数中,值不超过N的最低i位,且最高位不为0的数的个数 41 if (i < sz) ret += f[i][0][i >> 1][i >> 1][0][1]; // 由于数位长度不到sz,故高位用0补充,低位的部分可以超过N的最低i位,而总体(高位补0后长度为sz的数)不超过N,故这部分的数需要加上 42 } 43 return ret; 44 }; 45 return get(high) - get(low - 1); 46 } 47 };
参考资料
久违的力扣周赛讲解来啦~第111场双周赛:https://www.bilibili.com/video/BV1P44y1F7EG/
F - Nim Editorial:https://atcoder.jp/contests/abc317/editorial/7047
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17644193.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-08-20 炮兵阵地