数位DP

数位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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值