zoukankan      html  css  js  c++  java
  • 数位DP专题

    update 13-12-3

     hdu 4734 2013年成都网络赛G题

    A按位分解为 (AnAn-1An-2 ... A2A1)定义F(A)= An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1

    现在给你两个数A,B。问你在0~B区间内有多少个数<=F(A) (0 <= A,B < 109)

    dp[i][j]表示前i位的数值小于j的个数,则答案就是dp[n][F(A)]

    考虑如何转移:考虑第i位,如果当前位的数字为k,那么它的前面i-1位组成的数不能大于j-k*2^i。

    即dp[i][j]是由dp[i-1][j-k*2^i]过来的。

    枚举当前可以填入的数字k,求和就可以得到dp[i][j],即dp[i][j]= sum{dp[i-1][j-k*2^i]}

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 using namespace std;
     5 int dp[20][200000];
     6 int digit[20];
     7 int dfs(int pos,int num,bool flag)
     8 {
     9     if(pos==-1)
    10         return num>=0;
    11     if(num<0)
    12         return 0;
    13     if(!flag&&dp[pos][num]!=-1)
    14         return dp[pos][num];
    15     int ans=0;
    16     int end=flag?digit[pos]:9;
    17     for(int i=0; i<=end; ++i)
    18     {
    19         ans+=dfs(pos-1,num-i*(1<<pos),flag&&i==end);
    20     }
    21     if(!flag)
    22         dp[pos][num]=ans;
    23     return ans;
    24 }
    25 int solve(int x,int y)
    26 {
    27     int ans=0,pos=0;
    28     while(x)
    29     {
    30         ans+=(x%10)*(1<<pos);
    31         pos++;
    32         x/=10;
    33     }
    34     int len=0;
    35     while(y)
    36     {
    37         digit[len++]=y%10;
    38         y/=10;
    39     }
    40     return dfs(len-1,ans,1);
    41 }
    42 int main ()
    43 {
    44     int t;
    45     int a,b;
    46     scanf("%d",&t);
    47     memset(dp,-1,sizeof(dp));
    48     for(int Case=1; Case<=t; ++Case)
    49     {
    50         scanf("%d%d",&a,&b);
    51         printf("Case #%d: %d
    ",Case,solve(a,b));
    52     }
    53 }
    View Code

    =================================================================================================

    hdu 3555 

    给你一个区间[1,n],问你在这个区间中有多少个不包含49的个数,n最大为2^63-1。

    数位dp    dp[i][j]表示第i位状态为j时有多少个符合要求的数。

    状态j: 

    0:有49。

    1:pre没有49但pos+1为4。

    2:没有49。

    写法上面采用记忆化搜索的方法比较容易些。

    枚举每一位进行状态的转移。注意要使用longlong

     1 #include <iostream>
     2 #include <cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int digit[100];
     6 long long  dp[100][3];//dp[i][j]表示第i位,状态为j(0:有49;1:pre没有49但pos+1为4;2:没有49)
     7 long long  n;
     8 long long dfs(int pos,int state,bool flag)
     9 {
    10     if(pos==-1)
    11         return (state==0);
    12     if((!flag)&&(dp[pos][state]!=-1))
    13         return dp[pos][state];
    14     long long ans=0;
    15     int t = flag?digit[pos]:9;
    16     for(int i=0;i<=t;++i)
    17     {
    18         int s=state;
    19         if(state==2&&i==4)
    20             s=1;
    21         if(state==1&&i!=4)
    22             s=2;
    23         if(state==1&&i==9)
    24             s=0;
    25         ans+=dfs(pos-1,s,flag&&(i==t));
    26     }
    27     if(!flag)
    28         dp[pos][state]=ans;
    29     return ans;
    30 }
    31 long long  solve(long long  x)
    32 {
    33     int l=0;
    34     while(x)
    35     {
    36         digit[l++]=(x%10);
    37         x/=10;
    38     }
    39     return dfs(l-1,2,1);
    40 }
    41 int main ()
    42 {
    43     int T;
    44     scanf("%d",&T);
    45     while(T--)
    46     {
    47         scanf("%I64d",&n);
    48         memset(dp,-1,sizeof(dp));
    49         printf("%I64d
    ",solve(n));
    50     }
    51 }
    View Code

    hdu 2089

    给你一个区间[n,m],问这个区间中又多少个数不包含4与62(有4或有62都算包含),区间最大为10^6

    暴力也可以解,但数位DP更好。

    dp[i][j]的表示与上面一样。

    j状态:

    0:无4无62且上一位为6

    1:无4无62且上一位不为6

    2:有4无62前一位为6

    3:有4无62前一位不为6

    4:无4有62

    5:有4有62

    比较的多(也许我搞麻烦了),但仔细一点分类一下,画张图会比较清晰一点。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int digit[100];
     6 int  dp[100][10];
     7 int  n,m;
     8 int dfs(int pos,int state,bool flag)
     9 {
    10     if(pos==-1)
    11         return (state!=0&&state!=1);
    12     if((!flag)&&(dp[pos][state]!=-1))
    13         return dp[pos][state];
    14     int ans=0;
    15     int t = flag?digit[pos]:9;
    16     for(int i=0;i<=t;++i)
    17     {
    18         int s=state;
    19         if(i==2)
    20         {
    21             if(state==1)
    22                 s=4;
    23             else if(state==3)
    24                 s=5;
    25         }
    26         else if(i==4)
    27         {
    28             if(state==0)
    29                 s=2;
    30             else if(state==1)
    31                 s=2;
    32             else if(state==3)
    33                 s=2;
    34             else if(state==4)
    35                 s=5;
    36         }
    37         else if(i==6)
    38         {
    39             if(state==0)
    40                 s=1;
    41             else if(state==2)
    42                 s=3;
    43         }
    44         else
    45         {
    46             if(state==1)
    47                 s=0;
    48             else if(state==3)
    49                 s=2;
    50         }
    51         ans+=dfs(pos-1,s,flag&&(i==t));
    52     }
    53     if(!flag)
    54         dp[pos][state]=ans;
    55     return ans;
    56 }
    57 int solve(int x)
    58 {
    59     memset(dp,-1,sizeof(dp));
    60     int l=0;
    61     while(x)
    62     {
    63         digit[l++]=(x%10);
    64         x/=10;
    65     }
    66     return dfs(l-1,0,1);
    67 }
    68 int main ()
    69 {
    70     while(scanf("%d%d",&n,&m)!=EOF)
    71     {
    72         if(n==0&&m==0)
    73             break;
    74         printf("%d
    ",m-n+1-(solve(m)-solve(n-1)));
    75     }
    76 }
    View Code

    hdu 3652 2010年成都网络赛B题

    给你一个区间[1,n],问又区间内多少个数能被13整除且包含13,n最大为10^9

    记录一下前面模13的余数pre,其余的跟上面类似

    dp[i][j][k]表示第i位数字余数为j,状态为k(0:pre有13;1:pre没有13但pos+1为1;2:没有13)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int digit[20];
     6 int dp[20][15][3];//dp[i][j][k]表示第i位数字余数为j,状态为k(0:pre有13;1:pre没有13但pos+1为1;2:没有13)
     7 int n;
     8 int dfs(int pos,int pre,int state,bool flag)
     9 {
    10     if(pos==-1)
    11         return (state==0&&pre==0);
    12     if((!flag)&&(dp[pos][pre][state]!=-1))
    13         return dp[pos][pre][state];
    14     int ans=0;
    15     int t = flag?digit[pos]:9;
    16     for(int i=0;i<=t;++i)
    17     {
    18         int next=(pre*10+i)%13;
    19         int s=state;
    20         if(state==2&&i==1)
    21             s=1;
    22         if(state==1&&i!=1)
    23             s=2;
    24         if(state==1&&i==3)
    25             s=0;
    26         ans+=dfs(pos-1,next,s,flag&&(i==t));
    27     }
    28     if(!flag)
    29         dp[pos][pre][state]=ans;
    30     return ans;
    31 }
    32 int  solve(int x)
    33 {
    34     int l=0;
    35     while(x)
    36     {
    37         digit[l++]=(x%10);
    38         x/=10;
    39     }
    40     return dfs(l-1,0,2,1);
    41 }
    42 int main ()
    43 {
    44 
    45     while(scanf("%d",&n)!=EOF)
    46     {
    47         memset(dp,-1,sizeof(dp));
    48         printf("%d
    ",solve(n));
    49     }
    50 }
    View Code
  • 相关阅读:
    25.C++- 泛型编程之函数模板(详解)
    Windows10 + Visual Studio 2017 + CMake +OpenCV编译、开发环境配置及测试
    终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题
    Python的开源人脸识别库:离线识别率高达99.38%(附源码)
    python获取公网ip的几种方式
    Chrome与chromedriver.exe的版本对应
    Google Gson用法
    idea 报错javax/xml/bind/DatatypeConverter
    org.slf4j:slf4j-api:添加日志管理
    基本使用——OkHttp3详细使用教程
  • 原文地址:https://www.cnblogs.com/shuzy/p/3438229.html
Copyright © 2011-2022 走看看