题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1042
题目:
1042 数字0-9的数量
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题
给出一段区间a-b,统计这个区间内0-9出现的次数。
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次。
Input
两个数a,b(1 <= a <= b <= 10^18)
Output
输出共10行,分别是0-9出现的次数
Input示例
10 19
Output示例
1 11 1 1 1 1 1 1 1 1
题解:
求区间[a,b]各位数字出现次数并打印输出。
因为数据是10^18,暴力会T。
所以应该要找出出现的规律,优化算法。
从个位开始分析各个数位。
不妨把当前数位右侧的数都当作0来统计:
1、当前数位(cur):那么当前数位从1开始到cur,当前数位每增大1,这个数字出现次数是当前数位的权值value(如从11增大到12时,个位上1出现了value = 1次;从110增大到120时,十位上的1出现了value = 10次)
2、当前数位左边的数(res)也会影响当前数位:res每增大1,当前数位的数都会从0-9走遍历一遍,其中每次遍历在0-9的每个数字上产生的次数为value次。(如12300这个数,res = 123 ,value = 10,十位上的0-9产生的总次数均为 res * value = 1230 次)
3、当前数位还会影响当前数位左边的数:当前数位从0开始到cur,当前数位每增大1,当前数位左边的每个数均出现value次,所以总共会出现value * (cur + 1)次。(如12340这个数,cur = 4,value = 10。从12300增大到12349时,cur左边的1、2、3这三个数均会出现 value * (cur + 1) = 50次)
4、如果当前数位左边还有数,当前数位左移,继续统计。
AC代码:
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 typedef long long ll; 6 7 void dfs(ll res , ll value , ll LLSum[]) 8 { 9 ll cur = res % 10; 10 res /= 10; 11 ll t = res; 12 for (int i = 1 ; i <= cur ; i++) LLSum[i] += value; 13 for (int i = 0 ; i < 10 ; i++) LLSum[i] += res * value; 14 while (t) { 15 LLSum[t%10] += (cur + 1) * value; 16 t /= 10; 17 } 18 if (res) dfs(res-1,value * 10,LLSum); 19 return; 20 } 21 22 int main() 23 { 24 ll a,b; 25 ll Suma[10],Sumb[10]; 26 memset(Suma,0,sizeof(Suma)); 27 memset(Sumb,0,sizeof(Sumb)); 28 cin >> a >> b; 29 dfs(a-1,1,Suma); 30 dfs(b,1,Sumb); 31 for (int i = 0 ; i < 10 ; i++) { 32 cout << Sumb[i] - Suma[i] << endl; 33 } 34 return 0; 35 }
其实只要撸清各位数字之间的影响就可以了。
然后似乎还有数位DP的解法,待我再去研究一下。