数位dp

问题:在一个给定的区间[a,b]内,找满足要求的数。

如果我们用暴力的话一般要考虑数的大小,而数位dp要考虑的确实数的组成。

例如:  递增的  1234  1357  2468

          整除13的  13 26 39

          包含13的  132 2134

          双峰的   152630  243052

 

如果数的位数大于100,暴力肯定Over。

数位dp要注意的就是数的组成处理以及数的边界处理。

 

核心思路:记忆化搜索+记录合适状态。数位dp顾名思义就是逐位dp逐位考虑。

举例: [0,8457]内逐位递增的数有多少个。那么我们先对第一位进行处理,即枚举0XXX,1XXX,...8XXX。当我们枚举到第二位的时候。05XX,15XX,25XX...75XX,后面两位XX前的状态量都是5,如果我们已经处理了05XX后面满足要求的数的个数,那么我们15XX,25XX...是不是也可以利用那个已经求得的结果呢,利用深搜的那个结果可以用记忆化搜索预先求得。

开辟空间dp[len][state],表示最后len位前面状态为state时满足要求的数的个数。

 

hdu2089 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089

题目大意:求区间[a,b]内不含4和62的数有多少个。

思路: dp[len][state],state表示后面len位数之前一位那个数是否为6,是则为1不是则为0

 1 #include<iostream>

 2 #include<cstdio>

 3 #include<algorithm>

 4 #include<cstring>

 5 using namespace std;

 6 

 7 int dp[8][2];

 8 int digit[10];

 9 

10 int dfs(int len, int state, int fp)

11 {

12       if(!len) return 1;

13       if(!fp&&dp[len][state]!=-1) return dp[len][state];

14       int ret=0, fpmax=fp?digit[len]:9;

15       for(int i=0; i<=fpmax; i++)

16       {

17           if(i==4||(state&&i==2)) continue;

18           ret+=dfs(len-1,i==6,fp&&i==fpmax);

19       }

20       if(!fp) dp[len][state]=ret;

21       return ret;

22 }

23 

24 int cal(int n)

25 {

26     int len=0;

27     memset(dp,-1,sizeof(dp));

28     while(n)

29     {

30         digit[++len]=n%10;

31         n/=10;

32     }

33     return dfs(len,false,true);

34 }

35 

36 int main()

37 {

38     int a, b;

39     while(scanf("%d%d",&a,&b),a+b)

40     {

41         printf("%d\n",cal(b)-cal(a-1));

42     }

43     return 0;

44 }
View Code

 

hdu3652 

题目大意:求区间[a,b]内包含子串13以及是13的倍数的数有多少个。

思路:dp[len][pre][state],state表示后面len位数之前一位那个数是否为1,是则为1不是则为0,如果已经出现13则为2,pre记录前面各位数的余数。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3652

 

 

你可能感兴趣的:(dp)