Acwing 数位统计DP
Acwing 338.计数问题
输入样例:
1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0
输出样例:
1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
115 666 215 215 214 205 205 154 105 106
16 113 19 20 114 20 20 19 19 16
107 105 100 101 101 197 200 200 200 200
413 1133 503 503 503 502 502 417 402 412
196 512 186 104 87 93 97 97 142 196
398 1375 398 398 405 499 499 495 488 471
294 1256 296 296 296 296 287 286 286 247
实现思路:
-
定义函数:
count(n,x)
,其表示在1
到n
中,x
出现的次数(x
是0-9),那么,可以用类似前缀和的思想,来求解a
到b
中,x
出现的次数:count(b,x) - count(a-1,x)
-
以
x = 1
为例,如何计算count(n, 1)
:分情况讨论。比如n
是个7位的数字abcdefg
,我们可以分别求出1
在每一位上出现的次数,然后做一个累加即可。比如求1
在第4
位上出现的次数,求有多少个形如xxx1yyy
的数在1
到abcdefg
之间。分两种大情况:- xxx = 000 ~ abc - 1, 中间d=1,yyy = 000 ~ 999,一共有abc * 1000(即左右两边数的大小相乘)种选法
- 若xxx = abc
- d < 1,abc1yyy > adc0efg,超出n的范围,0种
- d = 1,yyy = 000 ~ efg,efg + 1种
- d > 1,yyy = 000 ~ 999,1000种
-
把上面全部的情况,累加起来,就是1出现在第四位的次数。
类似的,可以求解出1在任意一个位置上出现的次数,累加起来,就求出了1在每一位上出现的此时,即求解出了count(n,1)
-
注意:当
x=0
时,不能有前导0,所以当x=0
时,形如xxx0yyy
,前面的xxx
是从001(不能从00开始,左边选法相比不是0的情况就少了一次)
到999
具体实现代码(详解版):
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;//求n的位数
int get(int n){int res = 0;while(n) res ++,n /= 10;return res;
}//求1到n种i出现的次数int count(int n ,int i){int res = 0,dgt = get(n);//遍历每一位 求出每一位出现i的次数for(int j = 1 ; j <=dgt ; j ++){/*利用位运算得到当前遍历位次(第j位)的数的大小p:10^(j右边的位数即dgt-j)得到第j位左边的数大小l得到第j位右边的数大小r得到第j位上的数dj*/int p = pow(10,dgt - j), l = n / p /10,r = n % p,dj = n / p % 10;//然后分情况讨论 用i所在的位置划分出左边、右边/* 一、xxx..i..yyy的选法 左边取小于n中实际的左边的数l1)当i不为0时:左边000...~xxx...-1,右边...yyy就任意取了,取法:左边的数大小l*10^右边位数=l*p2)当i=0时,排除前导0的情况:左边000..1~xxx...-1,右边和上面一样,取法:(l-1)*p*/if(i) res += l * p;else res += (l - 1) * p;/* 二、左边固定为n的左边的数l1)、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(){int a,b;while(cin >> a >> b && a){if(a > b) swap(a,b);for(int i = 0; i <= 9 ; i ++){cout << count(b,i) - count(a-1,i)<<' ';}cout << endl;}return 0;
}
DP的难点就是分类讨论,可以具体拿个例子试一下。唉,多积累经验把~