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 }