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 }
时间: 2024-10-23 20:14:35