1042 数字0-9的数量
题目
分析
两种做法,
- 数位dp,这种比较好理解
- 还有迭代模拟
1. 数位dp
想好三点,
(例 2048)当前数字 e 是 204 时:
-
当前数字个位对个位影响 , 204 % 10 = 4, 【0,4】加上 s
-
当前数字高位对个位影响,204 / 10 = 20, 【0, 9】加上20 * s
-
当前数字个位对高位影响, 【2】加上 s * (4 + 1), 【0】加上 s * (4 + 1)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int M = 2e2 + 10;
const int N = 1e3 + 10;
ll arr[N], brr[N]; //存 0~9 出现次数
// s 代表当前数字的个位是 e 的哪一位,(个位,百位,千位……)
void dfs(ll s, ll e, ll c[]) // 1 到 e
{
ll n = e / 10; //高位
ll m = e % 10; //低位 当前位
ll t = n;
for(int i = 0; i <= m; i++){ //当前数字个位对个位影响
c[i] += s;
}
for (int i = 0; i < 10; i++){//当前数字高位对个位影响
c[i] += s * n;
}
c[0] -= s; //去除前缀零
while(t){ //当前数字个位对高位影响
c[t % 10] += s * (m + 1);
t /= 10;
}
if(n){ //n 已经处理过,从 n-1 递归
dfs(s * 10, n - 1, c);
}
}
int main()
{
ll a, b;
scanf("%lld%lld", &a, &b);
dfs(1, a-1, arr); //计算 1 到 a-1
dfs(1, b, brr); //计算 1 到 b
for(int i = 0; i < 10; i++){
printf("%lld\n", brr[i] - arr[i]);
}
return 0;
}
2. 迭代
分开计算0~9出现几次
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e2 + 10;
ll dp[N]; //18位
ll sum, len, cnt, tail, tnum;
ll cal(ll num, int x) //从 1 到 a,x出现次数, x 属于0~9
{
sum = len = tail = 0;
cnt = 1;
tnum = num;
while(num){
int tmp = num % 10; //当前数字的个位
len++;
if(tmp > x){ //
sum += dp[len - 1] * tmp + cnt;
}else if(tmp == x){
sum += dp[len - 1] * tmp + tail + 1;
}else{
sum += dp[len - 1] * tmp;
}
tail += tmp * cnt; //当前数字后面的数 eg:2048 , 当前 20 , tail 48
cnt *= 10; //进行到第几位
num /= 10;
}
if(!x){ //是 0 的话删除前缀零
ll ans = 1;
while(tnum){
sum -= ans;
ans *= 10;
tnum /= 10;
}
}
return sum;
}
int main()
{
for(int i = 1; i <= 18; i ++) { // 0 1 20 300 4000 50000 600000
dp[i] = dp[i-1]*10 + pow(10,i-1);
}
ll a, b;
cin >> a >> b;
for (int i = 0; i < 10; i++){
cout << cal(b, i) - cal(a - 1, i) << endl;
}
return 0;
}