zoukankan      html  css  js  c++  java
  • 数位dp(1)

    HDU 3555 Bomb

      题意:

        给你x,求小于等于x的所有正整数中包含49的数的个数。

      题解:

        最基础的数位dp。solve(x)表示小于等于x的数中包含49的数的个数。问题就在于如何实现solve函数。为了实现solve函数,我们需要使用一个dfs函数:dfs(x,b,lim)。返回值为满足条件的数的个数,x表示当前枚举的位数,b表示当前状态(b=2表示已经出现过49,b=1表示前一个数为4,b=0表示其他情况),lim是最重要的一点,即当前数位的取值有没有限制。

        我们举一个解释lim作用的例子,如果当前的数为5678,一开始我们调用dfs(4,0,1),所以当前位只能最大取到5,接下来我们从0到5枚举当前位的取值,如果当前位是小于5,接下来就没有限制,因为0XXX,1XXX,2XXX,3XXX,4XXX后面的那些XXX是可以从000取到999的,并不给它加以任何限制,然而对于5XXX,下一位只能取到0到6,因为57XX就已经大于当前的数了,所以lim还是只能设为1。

        接下来还有一个优化,可以发现如果lim=0,x和b相等的情况下答案是肯定相等的,而lim!=0则不一定,因为枚举的范围和当前数有关。所有我们可以用记忆化搜索的方法来优化复杂度。

        接下来给出代码: 

    #include<cstdio>
    #include<cstring>
    typedef long long ll;
    ll f[20][3]; int a[20],cnt;
    ll dfs(int x,int b,bool lim){
        if(x==0) return b==2; //搜到头了
        if(!lim&&f[x][b]!=-1) return f[x][b]; //记忆化搜索
        ll ans=0; int maxl=lim?a[x]:9; //maxl表示当前能枚举到的最大数
        for(int i=0;i<=maxl;++i) ans+=dfs(x-1,(b==2||(b==1&&i==9))?2:i==4,lim&&i==maxl); //注意这里的递归调用
        if(!lim) f[x][b]=ans; return ans;
    }
    ll solve(ll x){
        for(cnt=0;x;x/=10) a[++cnt]=x%10; //把这个数的每一位都存下来
        return dfs(cnt,0,1);
    }
    int main(){
        memset(f,-1,sizeof f); int t; scanf("%d",&t);
        for(ll x;scanf("%lld",&x)!=EOF;printf("%lld\n",solve(x))); return 0;
    }

      注意:

        如果有多组数据,不需要每组数据都将f数组memset一遍,因为当lim等于0时,dfs函数的结果和当前数字并没有关系。

        有很多数位dp题是求[l,r]中符合要求的数的个数,只要返回solve(r)-solve(l-1)就行了。

  • 相关阅读:
    OPC-UA和IEC 62541协议
    excel多级部门字符串截取其中一端的公式
    mac 显示音频文件 速率
    解决:Mac安装HOME brew 拒绝了我们的连接请求解决方案
    ss自定义规则
    macos关闭更新功能
    mac 使用触摸板左键长按选择是,总是弹出系统自带词典的问题
    固定区域截图快速粘贴到表格
    Outlook 2016 for Mac 更改邮件存放路径
    微信发送高清视频(避免被微信压缩变模糊),100M以内
  • 原文地址:https://www.cnblogs.com/jxcakak/p/7467979.html
Copyright © 2011-2022 走看看