• 数位dp知识


    转自http://blog.csdn.net/zhaoxinfan/article/details/8707605

    下面先给出数位DP的背景:

    •在给定区间[A,B]内,找满足要求的数。
    •要求一般和数大小无关,而与数的组成有关
    •例如,递增的,1234, 2579…
    •         双峰的,19280,26193…
    •         含49的,49, 149, 1492… 
    •         整除13的,26, 39…
    •麻烦在于,规模大,位数> 100,不能枚举。
    •区间往往不是整百整千,边界问题
    •注意
    –记忆化搜索思路清晰
    –开适当空间
    –寻找合适的状态,简化计算量

    为了降低时间复杂度,可以借鉴传统DP中状态转换,打表这些思路,得到了数位DP:

    F(A,B) = F(B,0)-F(A-1,0)

    暴力+存储 = 记忆化搜索

    •暴力:
    •暴力枚举每一位(0..9),注意区间边界;与符号的匹配。
     
    •dfs(i,j,k,flag)
    •枚举第i位的数,匹配str[j],前一位是k,是否达到上限(flag=true,false)
    •达到了上限则只能枚举0..num[i],否则可以枚举0..9
    •存储
    •dfs(i,j,k,flag)
    •设状态与递归参数一致f[i][j][k][flag],表示当枚举到第i位的数,匹配str[j],前一位是k,是否达到上限(flag=true,false)时,满足要求的数字个数。
    •dfs的过程,相当于在填充f,假设f的空间O(100*10*10*2),则dfs的时间O(20000)

    针对上面几种类型的问题,数位DP解决方案如下:(具体可以看http://www.cppblog.com/Yuan/archive/2011/07/15/139299.html

    •整除13
    •dfs(i, m, flag)
    •枚举第i位数,前面枚举出的数模13的余数m,是否到达上限flag
    •整除自身各位数CF55D
    •dfs(i, m, l, flag)
    •枚举第i位数,前面枚举出的数模LCM(0..9),LCM(前面枚举出的数),是否达到上限
    •包含”49”
    •dfs(i, k, find, flag)
    •枚举第i位数,前一位是k,是否已包含”49”(find),是否达到上限
    •分类讨论:前一位是否为4,当前是否已包含“49”
    在这几种类型中,包含49的与微软这道题最为相近,不过要注意的是运算过程中需要把前缀0的情况剔除,最终代码如下:
     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdlib>
     4 #include<cstring>
     5 using namespace std;
     6 typedef long long ll;
     7 
     8 #define mem(a,b) memset(a,b,sizeof(a))
     9 
    10 const int L = 20, P = 1e9+7;
    11 
    12 struct RES 
    13 {
    14     ll all, sum, cnt;
    15     RES() {}
    16     RES(int i,int j,int k):all(i),sum(j),cnt(k) {}
    17 } dp[L];
    18 
    19 ll chkmod(ll x,ll p) 
    20 {
    21     return (x%p+p)%p;
    22 }
    23 
    24 int d[L], n;
    25 
    26 RES dfs(int pos, int UP) 
    27 {
    28     if(pos<0)
    29     {
    30         return RES(0,0,1);
    31     }
    32     if(!UP && ~dp[pos].all)
    33     {
    34         return dp[pos];
    35     }
    36     RES ret(0,0,0);
    37     int up=UP?d[pos]:9;
    38     ret.all += dfs(pos-1, UP&&up==0).all;
    39     ret.all %= P;
    40     for(int i=1;i<=up;i++) 
    41     {
    42         int nUP = UP&&i==up;
    43         for(int j=pos-1;j>=-1;j--) 
    44         {
    45             ll tmp = dfs(j, nUP).sum + dfs(j, nUP).cnt * (pos - 1 - j);
    46             tmp %= P;
    47             ret.all += tmp;
    48             ret.all %= P;
    49             ret.sum += tmp;
    50             ret.sum %= P;
    51             ret.cnt += dfs(j, nUP).cnt;
    52             ret.cnt %= P;
    53 
    54             nUP = nUP && d[j]==0; // !!!
    55         }
    56     }
    57 
    58     if(!UP) 
    59     {
    60         dp[pos] = ret;
    61     }
    62     return ret;
    63 }
    64 
    65 ll cal(ll x) 
    66 {
    67     n=0;
    68     while(x) 
    69     {
    70         d[n++]=x%10;
    71         x/=10;
    72     }
    73     return dfs(n-1,1).all;
    74 }
    75 
    76 int main()
    77 {
    78     mem(dp,-1);
    79     ll n;
    80     while(cin>>n) 
    81     {
    82         cout<<cal(n)<<endl;
    83     }
    84     return 0;
    85 }
  • 相关阅读:
    双指针
    二维数组 查找常用字符
    将数组分为相等的三部分
    交换 排序
    买股票
    双指针 回文数
    双指针
    测试SM图床
    cnpm,vue等命令不识别的问题废人解决方案
    typeof面试题解答
  • 原文地址:https://www.cnblogs.com/shangyu/p/3671683.html
Copyright © 2020-2023  润新知