数位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 }

 

你可能感兴趣的:(dp)