BZOJ 1833 数字计数 数位DP

题目链接

做的第一道数位DP题,听说是最基础的模板题,但还是花了好长时间才写出来。。。。。

想深入了解下数位DP的请点这里

先设dp数组dp[i][j][k]表示数位是i,以j开头的数k出现的次数

有 

    

数位dp的题一般都会用到前缀数组,题目要求我们求b-a这个区间里各个数码出现的次数,我们可以分别求出(0,b)和(0,a-1)然后相减即可

具体分析请看代码,写的还算详细

 1 #include <bits/stdc++.h>
 2 using  namespace  std;
 3 typedef long long ll;
 4 ll dp[15][10][10];
 5 ll ans[10];//用来储存每个数码出现的次数 
 6 ll bin[15];//表示i位数中数码i出现的次数 
 7 ll d[15];//这个用来存储数的每一位 
 8 void rule(){
 9     bin[1]=1;
10     for(int i=2;i<14;i++) bin[i]=bin[i-1]*10;
11     for(int i=0;i<10;i++) dp[1][i][i]=1;
12     for(int i=2;i<=13;i++){
13         for(int j=0;j<=9;j++){
14             for(int z=0;z<=9;z++){
15                 for(int k=0;k<=9;k++)
16                 dp[i][j][z]+=dp[i-1][k][z];//比如i=2,j=2时,所求数字范围应该是200-299,这一步把0-99中各数码出现次数加进去 
17                 dp[i][z][z]+=bin[i-1];//这一位这是把200-299总共出现了100次的2给加进去 
18             }
19         }
20     }
21 }
22 void solve(ll x,int flag){
23     ll tmpx=x;//存储传进来的x 
24     int cnt=0;//记录x的位数 
25     memset(d,0,sizeof(d));
26     while(x){
27         d[++cnt]=x%10;
28         x/=10;
29     }
30     for(int i=1;i<cnt;i++){//这一步是最高位为0的,这些数都不会受到x的上限限制,都可以直接加进来 
31         for(int j=1;j<=9;j++){
32             for(int k=0;k<=9;k++)
33             ans[k]+=dp[i][j][k]*flag;
34         }
35     }
36     int tmpt=cnt;
37     while(cnt){//可以举个456的例子来仔细分析一下 
38         for(int i=0;i<d[cnt];i++){//注意这里是小于不是等于,保证上限不被取到,在后面再被处理 
39             if(!i&&cnt==tmpt) continue;//这种情况在最高位为0时已经统计过了,不能重复 
40             for(int j=0;j<=9;j++){
41                     ans[j]+=dp[cnt][i][j]*flag;//不是上限的时候都直接加 
42             }
43         }
44         ans[d[cnt]]+=(tmpx%bin[cnt]+1)*flag;cnt--;//随着while循环,上限一步步的被处理 
45     }//简述一下过程(456),就是第一个for处理了0-399,然后把首位4的57次加上,第二个for,处理的0-49......就这样一步步往下 
46 }
47 int  main(){  
48   ll a,b;scanf("%lld%lld",&a,&b);
49   rule();
50   solve(b,1);solve(a-1,-1);//1和-1是符号位,分别是加和减 
51   for(int i=0;i<10;i++)
52   printf("%lld%c",ans[i],i==9?'\n':' ');    
53   return 0;
54 }

 

posted @ 2019-02-13 14:07  清酒令  阅读(164)  评论(0编辑  收藏  举报