问题:在一个给定的区间[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 ",cal(b)-cal(a-1)); 42 } 43 return 0; 44 }
hdu3652
题目大意:求区间[a,b]内包含子串13以及是13的倍数的数有多少个。
思路:dp[len][pre][state],state表示后面len位数之前一位那个数是否为1,是则为1不是则为0,如果已经出现13则为2,pre记录前面各位数的余数。