码迷,mamicode.com
首页 > 其他好文 > 详细

数位dp(1)

时间:2017-09-02 22:35:49      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:数位dp   接下来   def   include   等于   代码   记忆化   div   一个   

HDU 3555 Bomb

  题意:

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

  题解:

    最基础的数位dp。设solve(x)表示小于等于x的数中包含49的数的个数,则我们需要求的就是solve(r)-solve(l-1)。问题就在于如何实现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)就行了。

数位dp(1)

标签:数位dp   接下来   def   include   等于   代码   记忆化   div   一个   

原文地址:http://www.cnblogs.com/jxcakak/p/7467979.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!