数位dp
我们要求[a,b]不包含49的数的个数,可以想到利用前缀和来做,具体来说
就是[a,b] = [0,b] - [0,a),(")"是不包括a),我们先求出给定a,b的每个位置的数,
保存在数组s中,例如a = 109,那么a[1] = 9,a[2] = 0,a[3] = 1.然后开始dp,
我们可以选择记忆化搜索或者是递推,前一种相对于第二种而言简单和较为容易理解一些,
所以我们选择记忆化搜索.那么需要记录些什么呢?首先长度是一定要记录的,
然后记录当前的数位是否为4,这样就便于在记忆化搜索中得到答案.
然后进行记忆化搜索,记录上一位是否为4和枚举这一位,如果没有限制的话很好办,
直接枚举就可以了,但是这样可能会超空间,因此我们每次都必须要判断是否有最大的限制,
这里不是很好说,看代码更容易理解:
int shu[100],dp[30][2];
//shu中存储输入数字的各个位所对应的数字
//dp[i][j]存储的是i位数字,最高位是j的数字中满足条件的数
//34298
int swdp(int len,bool if4,bool limit){
if(len==0) return 1;
if(!limit&&dp[len][if4])
return dp[len][if4];
//为什么要返回呢?可以画图理解当我们搜到3XXX时,
//程序运行到1XXX时就已经把3XXX之后的搜索完了,记忆化也是这个用意.
//因为XXX对于1和3这种非限制化数字来说是一样的,符合题意的个数相同
//所以不用在进行同样的循环而节省时间
int cnt= 0;mx =(limit?shu[len]:9);
//每一位严格小于输入的数
//limit标记该长度下枚举的范围
for(int i=0;i<=mx;i++)
{
if(if4&&i==9) continue;
cnt+=swdp(len-1,i==4,limit&&i==mx);//枚举判断
}
return limit?cnt:dp[len][if4]=cnt;
//如果有限制,那么就不能记忆化,否则记忆的是个错误的数.
//因为记忆化的是普遍数据,减少时间复杂度,而当limit有限制时为特殊情况只能单独计算
}
int solve(int x)
{
if(x==-1) return 0;
memset(shu ,0, sizeof(shu));
int len=0;
while(x>0)
{
shu[++len]=x%10;
x/=10;
}
return swdp(len ,false, true);
}
int main(){
int a, b;
cin>>a>>b;
cout<<solve(b)-solve(a-1)<<endl;
return 0;
}