• 数位DP


    问题:求某个区间[L,R]中满足某种限制条件的数的个数。

    暴力的思路:我们可以枚举[L,R]区间内部所有的数,依次判断每个数是否合法,若合法,就记下。

    显然,如果数据范围较大,达到1018甚至更大的级别的时候,这种暴力的算法无论时间还是空间都无法

    解决此类问题,所以我们引入数位DP。

    所谓数位DP,就是换一种枚举的方式,这种枚举的方式是基于数位(个位,百位,千位...)进行的,与

    数的大小没有任何关系。

    一般记忆化搜索版本要比数组递推版好实现。

    从一道题来引入

    (HDU2089)不要62

    题意:给定区间,求区间内部不含连续的62和4的数。

    数据范围 l,r<=10000000;

    我们考虑从最高位开始一位一位的进行枚举,运用记忆化搜索,如果枚举到最后一位所有位都合法,我们就找到了一个数。

    思考,前一位什么情况会对后一位的枚举产生影响:

    1,这位的前一位是6,这一位枚举了2,那么后面再枚举的情况都不再合法,我们去掉这种情况

    2,这一位枚举到4,后面枚举的情况都不合法,我们去掉这种情况。

    3,这一位枚举到了范围的最高位,那么下一位枚举的范围不能是0-9,而只能是0-这位的边界。

    *在数位DP的记忆化搜索中,记忆化的是穷举0-9的情况,最高位单独枚举并不会产生太大的复杂度。

    *数位DP运用到了前缀和的思想,我们统计1-R的答案,在统计1-L-1的答案,相减即为答案。

    代码实现来入门数位DP

    首先,我们要了解每一位在受到限制的情况下(即前一位枚举到最高位时)枚举的最高位。因此我们要把区间右端点拆分。

    1  int pos=0;
    2     while(k)
    3     {
    4         num[++pos]=k%10;
    5         k/=10;
    6     }

    然后就到了数位DP的模板精华部分。

     1 inline int dfs(int pos,int pre,int sta,bool limit)//pos:位数,pre:前驱,sta前一位是否是6,若不是则可以记忆化,否则因
     2                                                   //这一位2不能取而不能记忆化 limit:判断当前位的枚举上限 
     3 {
     4     if(pos==0) return 1;//表示找到了最低位仍合法证明找到了一个数 
     5     if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];//记忆化不是上限的0-9过程 
     6     int up,sum=0;
     7     if(!limit) up=9; else up=num[pos];//判断这位的枚举上限 
     8     for(int i=0;i<=up;i++)//枚举这一位的所有可能 
     9     {
    10         if(i==4) continue;//去掉4的情况 
    11         if(pre==6&&i==2) continue;//去掉前驱是6,这位是2的情况 
    12         sum+=dfs(pos-1,i,i==6,limit&&(i==num[pos]));//
    13     }
    14     if(!limit) dp[pos][sta]=sum;
    15     return sum;
    16 }

    类似的题还有windy数对于入门可以练习下。

    HDU2089完整代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    int dp[20][2],num[20];
    
    inline int dfs(int pos,int pre,int sta,bool limit)
    {
        if(pos==0) return 1;
        if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
        int up,sum=0;
        if(!limit) up=9; else up=num[pos];
        for(int i=0;i<=up;i++)
        {
            if(i==4) continue;
            if(pre==6&&i==2) continue;
            sum+=dfs(pos-1,i,i==6,limit&&(i==num[pos]));
        }
        if(!limit) dp[pos][sta]=sum;
        return sum;
    }
    
    inline int solve(int k)
    {
        int pos=0;
        while(k)
        {
            num[++pos]=k%10;
            k/=10;
        }
        return dfs(pos,0,0,true);
    }
    
    int main()
    {
        int m,n;
        memset(dp,-1,sizeof(dp));
        while(1)
        {
            scanf("%d%d",&m,&n);
            if(m==0&&n==0) return 0;
            printf("%d
    ",solve(n)-solve(m-1));
        }
        return 0;
    }
  • 相关阅读:
    使用top观察一进程的cpu历史占用情况
    djangoForm组件
    djangoAdmin组件
    js-metisMenu
    css-bootstrap
    flask环境安装
    python数据结构转换&格式化
    linux安装odbc for mysql
    json模块
    urllib模块
  • 原文地址:https://www.cnblogs.com/Hoyoak/p/11432776.html
Copyright © 2020-2023  润新知