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

 

posted @ 2014-04-17 19:30  _雨  阅读(353)  评论(0编辑  收藏  举报