One integer number x is called "Mountain Number" if:
(1) x>0 and x is an integer;
(2) Assume x=a[0]a[1]...a[len-2]a[len-1](0≤a[i]≤9, a[0] is positive). Any a[2i+1] is larger or equal to a[2i] and a[2i+2](if exists).
For example, 111, 132, 893, 7 are "Mountain Number" while 123, 10, 76889 are not "Mountain Number".
Now you are given L and R, how many "Mountain Number" can be found between L and R (inclusive) ?
Input
The first line of the input contains an integer T (T≤100), indicating the number of test cases.
Then T cases, for any case, only two integers L and R (1≤L≤R≤1,000,000,000).
Output
For each test case, output the number of “Mountain Number” between L and R in a single line.
Sample Input
3
1 10
1 100
1 1000
Sample Output
9
54
384
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,l,r,dp[15][15][2],b[15];
//v当前位置,pre前一个数字,odd_1=奇数_0=偶数,flag_1达到上限;
int seek(int v,int pre,int odd,int flag)
{
if(v==-1)return 1;
if(dp[v][pre][odd]!=-1&&!flag)return dp[v][pre][odd];//dp exists on flag;
//dp若有值一定>0,故若在main外定义可免去初始化,就让其为0;
int lim=flag?b[v]:9;
int res=0;
for(int i=0;i<=lim;i++)
{
if(odd&&i<=pre)res+=seek(v-1,i,!odd,flag&&i==b[v]);
else if(!odd&&i>=pre)res+=seek(v-1,i,!odd,flag&&i==b[v]);
}
if(!flag)dp[v][pre][odd]=res;//if(flag) informal can't be tagged;
return res;
}
int solve(int x)
{
if(x==0)return 1;
int ans=0;
while(x)
{
b[ans++]=x%10;
x/=10;
}
return seek(ans-1,9,1,1);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&l,&r);
memset(dp,-1,sizeof(dp));
//由于解有solve(r)-solve(l-1)得到,整个数据中dp值是一样的,故其实不必memset;
printf("%d\n",solve(r)-solve(l-1));//solve(r)-solve(l-1);
}
return 0;
}
下面是原本的代码,是解释了题意的代码,逻辑上比第一种代码正确;
关于seek过程中出现前导0的情况,eg. 2 ~456 中的20;若不判前导0,则此时20为020,是mountain number,但其实应为20不是Moun Num;
#include<iostream>
#include<cstdio>
using namespace std;
int T,L,R,bit[15],dp[15][15][2];
//zero=1则有前导0; eg. 0~278中的(0)20;
int seek(int v,int pre,int odd,int zero,int flag)
{
if(v<0)return 1;
if(!flag&&dp[v][pre][odd])return dp[v][pre][odd];
int lim=flag?bit[v]:9;
int res=0;
/*本来以为下面对前导0的判定可简化,但是没成功*/
for(int i=0;i<=lim;i++)
{
if(zero==1&&i==0)res+=seek(v-1,9,odd/*若i为0,一直是奇数位*/,zero,0);
else if(odd&&i<=fig)res+=seek(v-1,i,!odd,0,flag&&i==bit[v]);//
else if(!odd&&i>=fig)res+=seek(v-1,i,!odd,0,flag&&i==bit[v]);//
}
if(!flag)dp[v][pre][odd]=res;
return res;
}
int solve(int x)
{
int i=0;
while(x)
{
bit[i++]=x%10;
x/=10;
}
return seek(i-1,9,1,1,1);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&L,&R);
printf("%d\n",solve(R)-solve(L-1));
}
return 0;
}
对前导0的判定只能做到简化部分
if(zero==1&&i==0)res+=seek(v-1,9,odd/*若i为0,一直是奇数位*/,zero&&!i,flag&&i==bit[v]);
else if(odd&&i<=fig)res+=seek(v-1,i,!odd,zero&&!i,flag&&i==bit[v]);//
else if(!odd&&i>=fig)res+=seek(v-1,i,!odd,zero&&!i,flag&&i==bit[v]);//
对于odd和pre,目前不知道如何简化,有可能只是数学中数字的神奇,但总觉得可以透过表面简化成第一种代码;待参;