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)就行了。

  • 相关阅读:
    37. Sudoku Solver(js)
    36. Valid Sudoku(js)
    35. Search Insert Position(js)
    34. Find First and Last Position of Element in Sorted Array(js)
    33. Search in Rotated Sorted Array(js)
    32. Longest Valid Parentheses(js)
    函数的柯里化
    俞敏洪:我和马云就差了8个字
    vue路由传值params和query的区别
    简述vuex的数据传递流程
  • 原文地址:https://www.cnblogs.com/jxcakak/p/7467979.html
Copyright © 2011-2022 走看看