题目:
数位dp详解
数位dp就是利用数组的深搜实现对0~nums区间内任意数字的连续操作,而dp就是进行了记忆化搜索。
//首先要清楚数位dp的实质:对该数组形式存储的数字,对它的0~nums的所有数字进行相应的操作。这个时候就可想到数位dp。
int a[12];//存储的数字的十进制 位数。
//memo数组用于处理重复性记忆问题(备忘录)。记住还有limit变量用于判断是否被限制状态。
//这里备忘录后面存储count的长度根据需求来,比如输入的十进制数字最大也就10位,则count肯定不会超过10位。
int memo[12][10];//不能再这里直接初始化,不然会出错!!最好写个init函数
//这里的备忘录的含义是:当处于非限制状态的同一层数字情况(相当于p层),且count所记录下的数字相同。
int dp(int p,int count,bool limit){
//撒网到尽头返回该条路径的count,后面由res将网收拢网上继续收网。
if(p<0)
return count ;
//对于既是同层撒网,且网中的初始count相同,并且处于非限制状态的。也就是既是非限制状态又处于备忘录,则可返回。
if(!limit&&memo[p][count]!=-1)return memo[p][count];
//注意每层(相当于每位)都要刷新res,每一层的结果都是孤立的通过+=联系起来
int res = 0;
//如果处于限制状态则该位最多取a[p]否则0-9
int up = limit?a[p]:9;
for(int i=0;i<=up;i++){
//每次选该位数字的下一种情况时,重置cur
int cur = count;
if(i==1)
cur++;
res += dp(p-1,cur,limit && i==a[p]);
}
if(!limit) memo[p][count] = res;
return res;
}
void init(){
memset(memo,-1,sizeof(memo));
}
//solve函数用于解决该问题。nums低位对应a数组的0位
int solve(int nums){
int x = nums;
int p = 0;
while(x){
//将数位按数组存储
a[p++]=x%10;
x /= 10;
}
return dp(p-1,0,true);
}
int countDigitOne(int n){
init();
return solve(n);
}