[ZJOI2010]数字计数

题目描述

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

输入文件中仅包含一行两个整数a、b,含义如上所述。

输出格式

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

输入输出样例

输入 #1
1 99

输出 #1
9 20 20 20 20 20 20 20 20 20

说明/提示

30%的数据中,a<=b<=10^6;

100%的数据中,a<=b<=10^12。

【解题思路】

关于数位dp,(数位dp的题只有dfs才是通解) 个人认为dfs代码要比递推的代码简洁不少,而且更易理解。

题解已经有一个四维的dfs了,实际上这个题数组只需要开两维,可以省掉两维,于是发一个二维的。

先说下思路,对于这道数位dp的题我们考虑用记忆化搜索来解决。定义num数组来拆数,显然需要bool limit和lead来分别判断是否达有限制和是否有前导0。有前导0设为0,有限制为1。具体请看代码。

【code】

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 long long a , b;
 7 long long  dp[20][20],num[20];
 8 long long  dfs(int pos,bool limit,bool lead,int dig,long long sum)
 9 //sum是一定需要存的一个变量,因为她表示的是操作答案,需要不断随dfs更新
10 {//limit为1表示有限制,lead为0表示有前导0
11     long long ans=0;
12     if (pos==0) return sum;//边界条件
13     if(!limit&&lead&&dp[pos][sum]!=-1) return dp[pos][sum];//记忆化
14     int up=9;
15     if(limit) up=num[pos];//达到限制就设上限为当前位的数(不想用三目运算符)(雾)
16     for(int j=0;j<=up;j++)
17         ans+=dfs(pos-1, (j==up)&&limit , lead||j , dig , sum+((j||lead)&&(j==dig)) );
18     if(!limit&&lead) dp[pos][sum]=ans;
19     return ans;
20 }//数位dp的操作
21 long long  work(long long x,int d)
22 {
23     memset(dp,-1,sizeof(dp));//初始化
24     int len=0;
25     while(x)
26     {
27         num[++len]=x%10;//最高位在第一位 
28         x/=10;
29     }
30     return  dfs(len,1,0,d,0);
31 }
32 int main()
33 {
34     cin>>a>>b;
35     for(int i=0;i<=9;i++)//九个digit
36     {
37          if(i!=9)cout<<work(b,i)-work(a-1,i)<<" ";
38          else cout<<work(b,i)-work(a-1,i);
39          //我也不知道最后边输出了空格会不会炸(手动滑稽)
40     }
41     return ^-^;
42 }

 

posted @ 2019-07-25 21:32  GTR_PaulFrank  阅读(302)  评论(0编辑  收藏  举报