计数器

Problem description

一本书的页数为N,页码从1开始编起,请你求出全部页码中,用了多少个0,1,2,…,9。其中一个页码不含多余的0,如n=1234时第5页不是0005,只是5。

Input format 

一个正整数N(N<=10^9),表示总的页码。

Output format

共十行,第K行表示数字K-1的个数。

Algorithm design

模拟TLE/记忆化搜索AC

Problem analysis

模拟TLE

先敲一下暴力,方便对拍

从1枚举到n,每次将该数各位切分后加入答案、

O(nlog10n)

隐约觉得在切分的时候可以有些许优化把log10n提高一下

但不用想了复杂度里有n本身肯定过不了

记忆化搜索AC

由计数问题得到的经验:对于较大整数状态转移不是去头就是去尾

一开始想着去头,发现0不能做数头的问题约束了这个算法

所以采取去尾状态转移法,去掉尾之后如果num成了0直接退出

那么怎么状态转移呢?

不难发现,对于一个数如234而言

可以拆分为尾是0~9的数,又分为两大类:

1.对于尾是0~4 那么十位数就可以从1枚到23

2.对于尾是5~9 那么十位数就可以从1枚到22

之后以此类推,直到1位数无法拆分

既然如此就可以看出动态转移的基本模型了

但是,在确定尾并不是被真正“去”掉了,而只是在确定之后不参与枚举

那么答案数列里还要考虑枚举个数个尾

至于枚举个数可以很容易的写出来,num%10(-1)

想到这里还是简单递归,顶多比模拟多10分

再看有许多低位数字被反复的使用,自然联想出记忆化思想

因为要记忆化,图方便就对于0~9依次搜索

这样记忆化只用开一维数组,且可以直接复制

至于记忆化的范围,我觉得其实把常用的2,3位数记忆化就行了

但在编程中要有“得寸进寸”的思想,在确保不会爆空间的情况下尽可能记得多

所以我对于记忆化开了10^7..

这样就很容易过了

Source code

记忆化搜索

 1 #include <bits/stdc++.h>
 2 #define lim 10000000
 3 
 4 using namespace std;
 5 
 6 int n,ans[10],opt[10000001];
 7 //opt[i]表示数字1~i可拆分出的某个数字个数
 8 
 9 void input()
10 {
11     freopen("test.in","r",stdin);
12     freopen("test.out","w",stdout);
13     cin>>n;
14     return ;
15 }
16 
17 int search(int tar,int num)
18 {
19     if(num<1)
20         return 0;
21 //如果要拆分的数小于1,则拆分无意义
22     if(num<=lim&&opt[num])
23         return opt[num];
24 //如果可以记忆并被记忆过,则直接返回
25     int re=0;
26     if(tar&&tar<=num%10)
27         re=num/10+1;
28 //如果不为零,并且寻找的数不大于个位数,就可以考虑前面是0~num/10
29     else 
30         re=num/10;
31 //如果为零,就不用考虑前面为0,如果大于个位数,就不用考虑num/10
32     re+=(num%10+1)*search(tar,num/10)+(9-num%10)*search(tar,num/10-1);
33 /*
34 对于不大于个位数的数字都可以延伸1~num/10
35 大于个位数的数字不考虑num/10
36 */
37     if(num<=lim)opt[num]=re;
38 //可以记忆再记忆,不然就会数组越界
39     return re;
40 }
41 
42 void output()
43 {
44     for(int i=0;i<=9;i++)
45         cout<<ans[i]<<endl;
46 //输出答案
47     fclose(stdin);
48     fclose(stdout);
49     return ;
50 }
51 
52 int main()
53 {
54     input();
55     for(int i=0;i<=9;i++)
56     {
57         memset(opt,0,sizeof(opt));
58         ans[i]+=search(i,n);
59 //针对0~9依次搜索
60     }
61     output();
62     return 0;
63 }
View Code

over~

原文地址:https://www.cnblogs.com/qswx/p/9308521.html