数位统计DP
参照大佬的博客.,自己写出的代码(比较复杂, 大佬的博客非常简洁)
核心思想:
可以不用vector存每一位,直接计算某位的左边和右边的整数是多少。
可以不用讨论那么多细枝末节,只需要知道,当i为0时其左边整数不能为0,就够了。
思路:求一个数字第i
位上x
出现的次数, 遍历所有位, 就可以计算出整个数num
上, x
出现的总次数
考虑情况: abc x def
(其中abc
泛指 x
的左边的高位数字, def
泛指 x
的右边的低位数字)
那么, p
代表 pow(10, i)
, 即x
所在位置的那个模, cur
代表当前数字 num
中第i
位的数字
(1) 考虑对于高位, x
在第 i
位出现的次数
1) x != 0 : 出现次数位000 ~ abc - 1 ,总共为 abc * p
2) x == 0 : 出现次数为 001 ~ abc - 1, 总共为 (abc - 1) * p
(2) 考虑对于低位,x
在第i
位出现的次数
1) cur > x : 那么x可以出现的次数为 000 ~ 999 ,总共为p次
2) cur == x : 那么x可以出现的次数为000 ~ def, 总共为def + 1次
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int a, b;
// 获取num的位数
int get(int num){
int res = 0;
while(num){
++res;
num /= 10;
}
return res;
}
// 求num中x出现的次数
int count(int num, int x){
int res = 0, dgt = get(num);
for(int i = 0; i < dgt; ++i){
//
/* p为当前遍历位次(第i位)的数大小 <10^(右边的数的位数)>,
l为第i位的左边的数,r为右边的数,cur为第i位上的数 */
int p = pow(10, i), l = num / p / 10, r = num % p, cur = num / p % 10;
// 如果当前不是最高位
if(i != dgt - 1){
if(x) res += l * p;
else res += (l - 1) * p;
if(cur > x) res += p;
else if(cur == x) res += r + 1;
}else{ // 当前是最高位
if(x){ // x != 0 只有当x不为0的时候,我们才计算其出现的次数,因为最高位不能为0
if(cur > x) res += p;
else if(cur == x) res += r + 1;
}
}
}
return res;
}
int main(){
while(scanf("%d%d", &a, &b), a || b){
if(a > b) swap(a, b);
for(int i = 0; i < 10; ++i)
printf("%d ", count(b, i) - count(a - 1, i));
puts("");
}
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int a, b;
int get(int num){
int res = 0;
while(num){
++res;
num /= 10;
}
return res;
}
int count(int n, int i) { // 求从1到数n中数i出现的次数
int res = 0, dgt = get(n);
for (int j = 1; j <= dgt; ++ j) {
/* p为当前遍历位次(第j位)的数大小 <10^(右边的数的位数)>,
l为第j位的左边的数,r为右边的数,dj为第j位上的数 */
int p = pow(10, dgt - j), l = n / p / 10, r = n % p, dj = n / p % 10;
// ps:下文的xxx、yyy均只为使读者眼熟,并不严格只是三位数啊~ 然后后续的...就代表省略的位数啦~
/* 求要选的数在i的左边的数小于l的情况:
(即视频中的xxx1yyy中的xxx的选法) --->
1)、当i不为0时 xxx : 0...0 ~ l - 1, 即 l * (右边的数的位数) == l * p 种选法
2)、当1位0时 由于不能有前导零 故xxx: 0....1 ~ l - 1,
即 (l-1) * (右边的数的位数) == (l-1) * p 种选法 */
if (i) res += l * p;
else res += (l - 1) * p;
/* 求要选的数在i的左边的数等于l的情况:(即视频中的xxx == l 时)
(即视频中的xxx1yyy中的yyy的选法)--->
1)、i > dj时 0种选法
2)、i == dj时 yyy : 0...0 ~ r 即 r + 1 种选法
3)、i < dj时 yyy : 0...0 ~ 9...9 即 10^(右边的数的位数) == p 种选法 */
if (i == dj) res += r + 1;
if (i < dj) res += p;
}
return res; // 返回结果
}
int main(){
while(scanf("%d%d", &a, &b), a || b){
if(a > b) swap(a, b);
for(int i = 0; i < 10; ++i)
printf("%d ", count(b, i) - count(a - 1, i));
puts("");
}
return 0;
}