【hdu6148】Valley Numer【数位dp模板题】
题意
对于每组数据给出一个整数n(length(n)<=100),找出不大于n的数字中有多少是Valley Numer。对于Valley的定义是它每一位的数字要么是递增,要么是递减,要么是先递减后递增
分析
选拔赛的时候遇到了数位dp,以前只是听说但是没学过今天就拿出来一天的时间学了一下数位DP。
我们发现单调递减变为单调递增合法,单调递增变为递减不合法,所以通过增减判断最多只有一个拐点。那么我们在dp的时候状态只要记录当前是递增还是递减就可以了。
我们用dp[p][pre][state]来进行记忆化。p代表的是位数,pre代表的是上一位的值,state代表是递减还是递增。
这个算是数位dp很板子的一个题目,但是我一开始一直wa,感觉自己考虑的很对,然后最后看题解发现自己没有考虑前导0.
我们发现,当存在前导0的时候,相当于从这里新开一个数字,那么当前是递增还是递减就不确定了,没法根据前面的0来判断,所以当存在前导0的时候步可以记忆化否则会因为dp数组的冲突而导致答案错误。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 6 using namespace std; 7 typedef long long LL; 8 const int mod=1000000007; 9 const int maxn=100+10; 10 int n,a[maxn],pos,T; 11 char s[maxn]; 12 LL dp[maxn][10][2];//0 上升 1 下降 13 LL dfs(int p,int pre,int state,int lead,int limit){ 14 if(p==0){ 15 if(lead) 16 return 0; 17 return 1; 18 } 19 if(!limit&&dp[p][pre][state]!=-1&&!lead) 20 return dp[p][pre][state]; 21 int up=limit?a[p]:9; 22 LL res=0; 23 for(int i=up;i>=0;i--){ 24 if(state==0&&i<pre) 25 continue; 26 int st; 27 if(i==pre)st=state; 28 else if(i<pre)st=1; 29 else st=0; 30 if(lead)st=1; 31 res=(res+dfs(p-1,i,st,lead&&i==0,limit&&i==a[p]))%mod; 32 } 33 if(!limit&&!lead) 34 dp[p][pre][state]=res; 35 return res; 36 } 37 LL solve(){ 38 pos=0; 39 for(int i=n-1;i>=0;i--){ 40 pos++; 41 a[pos]=s[i]-'0'; 42 } 43 memset(dp,-1,sizeof(dp)); 44 return dfs(pos,0,0,1,1); 45 } 46 int main(){ 47 scanf("%d",&T); 48 for(int t=1;t<=T;t++){ 49 scanf("%s",s); 50 n=strlen(s); 51 printf("%lld\n",solve()%mod); 52 } 53 return 0; 54 }