FZU 2109 Mountain Number 數位DP

NO IMAGE
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,目前不知道如何簡化,有可能只是數學中數字的神奇,但總覺得可以透過表面簡化成第一種程式碼;待參;