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