POJ 3286 How many 0's?

How many 0's?
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 2591   Accepted: 1349

Description

A Benedict monk No.16 writes down the decimal representations of all natural numbers between and including m and n, mn. How many 0's will he write down?

Input

Input consists of a sequence of lines. Each line contains two unsigned 32-bit integers m and n, mn. The last line of input has the value of m negative and this line should not be processed.

Output

For each line of input print one line of output with one integer number giving the number of 0's written down by the monk.

Sample Input

10 11
100 200
0 500
1234567890 2345678901
0 4294967295
-1 -1

Sample Output

1
22
92
987654304
3825876150

一道数位DP题,昨天下午3点半开始写,足足写道晚上11点半才AC啊(除去晚上去上了两个小时的大学物理解题法)
主要就是状态转移一直都搞得比较混乱,所以总是求不对
本题中设置了两个数组dp[i]和dp0[i],dp[i]中存的是从0到99……9(共i个9)中0的个数(不含前导0,即0就是0,12就是12);dp0[i]中存的是从00^0(共i个0)到99……9(共i个9)中0的个数(含前导0,比如dp[4]中,0是0000,12是0012)
之后将dp[]和dp0[]初始化(详见代码)
  这里的状态转移就很重要(在这里错了好久……)
  含前导0的i位数字就是由i-1位的数字前面分别加上0~9构成的,所以dp0[i]=dp0[i-1]*10+power(10,i-1),前面的dp0[i-1]表示i-1位的那些数字被用了10次,里面的0又被写了10次,后面加上的10的i-1次幂表示在i-1位的数字前面加0时,一共加了10的i-1次幂个0
  不含前导0的i位数字是由不含前导0的i-1位数字和含前导0的i-1位数字前面加1~9构成的,所以dp[i]=dp[i-1]+dp0[i-1]*9
将给出的数字分解成一位一位地保存到数组num[]中,从最高位开始DFS(下面以400357为例说明):
  400357可以分解成3部分来写,1~99999,100000~399999和400000~400357,所以要分三部分来求
    第一部分显然就是dp[5]
    第二部分为(4-1)倍的dp0[5],在含前导0的五位数钱分别写1,2,3(注意4并不能全部写完,有限制,故需要单独讨论)
    前两部分比较容易理解,第三部分则需要DFS递归求解:long long dfs(int pos,long long a)
      如果当前搜索的这位上是0(即num[pos]==0),那么就是后面那些位中含0的个数dfs(pos-1,400357)加上这个0被写的次数(a%power(10,pos-1)+1)
      如果当前搜索的这位上不是0(即num[pos]!=0),那么这个数字要做和上面类似的分解(假设算到了400357中的3这位),那么分解为000~099,100~299,300~357。第一部分就是在含前导0的两位数前面写0,容易知道是power(10,pos-1)+dp0[2];第二部分就是在含前导0的两位数前面分别写1,2,易知是(3-1)*dp0[2];第三部分根据DFS递归的定义,就是dfs(2,400357)
至此本题的精髓解释完毕~~~~
其他细枝末节还要依照题目稍为修改

下面是C++源代码:
[C++]
 1 #include<iostream>
 2 
 3 using namespace std;
 4 
 5 int num[11];
 6 //例如:dp[4]为从0到9999中0的个数,dp0[4]为从0000到9999中0的个数
 7 long long dp[11],dp0[11];
 8 
 9 long long power(int a,int b)
10 {
11     long long result=1;
12     for(int i=1;i<=b;i++)
13         result*=a;
14     return result;
15 }
16 
17 void initial()
18 {
19     dp0[1]=dp[1]=1;
20     for(int i=2;i<11;i++)
21     {
22         dp0[i]=dp0[i-1]*10+power(10,i-1);
23         dp[i]=dp0[i-1]*9+dp[i-1];
24     }
25 }
26  
27 //pos为当前要搜的位,a为要计算的那个数字(注意是整个数字而不是正在搜索的部分)
28 long long dfs(int pos,long long a)
29 {
30     if(pos==1)
31         return 1;
32     if(num[pos]==0)
33         return a%power(10,pos-1)+1+dfs(pos-1,a);
34     else
35         return dp0[pos-1]*(num[pos]-1)+dfs(pos-1,a)+power(10,pos-1)+dp0[pos-1];
36 }
37 
38 long long cal(long long a)
39 {
40     int pos=0;
41     long long temp=a;
42     if(temp<=0)
43         return temp<0?0:1;
44     while(temp)
45     {
46         num[++pos]=temp%10;
47         temp/=10;
48     }
49     if(pos==1)
50         return 1;
51     else
52         return dp[pos-1]+dp0[pos-1]*(num[pos]-1)+dfs(pos-1,a);
53 }
54 
55 int main()
56 {
57     long long a,b;
58     
59     initial();
60 
61     while(cin>>a>>b)
62     {
63         if(a==-1&&b==-1)
64             break;
65         else
66             cout<<cal(b)-cal(a-1)<<endl;
67     }
68 
69     return 0;
70 }

 

posted @ 2013-05-08 06:54  ~~Snail~~  阅读(435)  评论(0编辑  收藏  举报