hdu 6148 Valley Numer (数位dp)

Problem Description
众所周知,度度熊非常喜欢数字。

它最近发明了一种新的数字:Valley Number,像山谷一样的数字。
当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。
比如,1,10,12,212,32122都是 Valley Number。
121,12331,21212则不是。
度度熊想知道不大于N的Valley Number数有多少。
注意,前导0是不合法的。
 
Input
第一行为T,表示输入数据组数。
每组数据包含一个数N。
● 1≤T≤200
● 1≤length(N)≤100
Output
对每组数据输出不大于N的Valley Number个数,结果对 1 000 000 007 取模。
Sample Input
3
3
14
120
Sample Output
3
14
119
 
题意:让你统计小于n的满足各个数位上不会出现先上升再下降的数字的个数
一道基本的数位dp
代码如下:
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn = 110;
 5 const ll mod = 1e9+7;
 6 char s[maxn];
 7 int dig[maxn];
 8 ll dp[110][10][2];
 9 ll dfs (int pos,int now,int up,int limit,int lead)//pos代表当前位 now代表上一位的数字 up=1表示到该位是上升状态
10                                                   //limit=1表示前几位贴着n的上界 lead=1表示当前状态有前导零
11 {
12     if (pos<0){         //如果dp到了最低位
13         if (lead) return 0; //如果当前结果是含有前导零的不计数
14         else return 1;  //否则计数
15     }
16     if (!limit&&!lead&&dp[pos][now][up]!=-1)    //如果当前的dp状态没有到达给的数的上界
17                                                 //而且,当前状态没有前导零而且之前计算过
18         return dp[pos][now][up];                //记忆化搜索直接返回
19     ll ans = 0;
20     int top;    //当前位能达到的最大值
21     if (limit) top = dig[pos];  //如果之前几位都达到了给的n的对应位的上限,此时这一位最大值就是n的pos位
22     else top = 9;               //如果前面几位没有贴着n的上界,那么这一位不管整个数怎么取都不会大于n,自然这一位最大值为9
23     for (int i=0;i<=top;++i){   //
24         if (up&&i<now) continue;    //如果之前的up为上升状态而这一位比上一位小不满足题意直接过掉
25         int nxtup;  //下一步up的状态
26         if (up||((i>now)&&(!lead))) //下一步是上升状态有两种情况 1.前几位已经是上升的了
27                                     //注意到这里已经保证如果up==1,那么此时i>=now
28                                     //2.这一位比上一位大而且上一位还不是0(前导零,比如002这样是不算上升的)
29         nxtup=1;                    //满足上面2个条件的任意一个的话下一步就是上升状态的
30         else nxtup=0;//否则下一步上升up为0
31         int nxtlead;    //下一步前导零的状态
32         if ((i==0)&&lead) nxtlead = 1;//当且仅当前几位有前导零而且这一位i还是0,下一步还是有前导零的
33         else nxtlead = 0;   //否则下一位就没有前导零
34         int nxtlimit;   //下一步上界的状态
35         if (limit&&i==dig[pos]) nxtlimit = 1;//当且仅当前几位一直贴着n的上界而且这一位还是贴着n的上界,此时下一步状态还是贴着上界
36         else nxtlimit = 0;//否则下一步就不贴着n的上界了
37         (ans+=(dfs(pos-1,i,nxtup,nxtlimit,nxtlead)))%=mod;//向下dp一位
38     }
39     if (!limit&&!lead)  //如果当前没到达边界而且没有前导零,这样的是有资格存进dp数组的
40                         //因为dp[pos][now][up]存的是到pos位,当前数字为now,上升状态为up,没有到达上界而且没有前导零的数目
41         return dp[pos][now][up]=ans;
42     return ans;//如果没有资格直接返回答案计数就行了
43 }
44 int main()
45 {
46     int T;
47     //freopen("de.txt","r",stdin);
48     scanf("%d",&T);
49     memset(dp,-1,sizeof dp);
50     while (T--){
51         scanf("%s",s);
52         int len = strlen(s);
53         for (int i=0;i<len;++i)
54             dig[i]=s[i]-'0';
55         reverse(dig,dig+len);
56         cout<<dfs(len-1,0,0,1,1)<<endl;
57     }
58     return 0;
59 }

 

posted @ 2017-08-18 22:32  抓不住Jerry的Tom  阅读(659)  评论(0编辑  收藏  举报