原题:
Problem Description
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
Sample Input
Sample Output
题意+题解:
非常基础的一道i数位DP的题目,也是我写的第一道数位DP,各种无力。。。。
原题是说 有一种数叫做 Mountain Number,当其偶数位大于等于两边,奇数位小于等于两边时。
然后有K次询问。K<100。问L到R区间内有多少个Mountain Number。 L,R<10^9
显然无法用正常的枚举,10^9,即使是O(n)也要超。
这种询问区间内符合条件的数的个数是典型的数位DP的问法。说实话,我也是稀里糊涂就A了,因为这题要考虑的条件比较少,初值为0的情况下即使多进行了多余的DP转移也不会影响答案。。。所以我目前对数位DP的理解依旧不清晰。
回到此题。典型的做法是分别求[0,L]和[0,R]内的符合条件的个数,再相减,注意判断边界值的问题。这样进行DP时,不用考虑下界的问题,比较方便。
定义状态 dp[i][j][0/1] 表示当前第i位上,选择为j时所能求得的所有方案,[0/1]表示之前的上一位有没有触顶,因为要考虑上届的问题。
所以i只有10表示数位个数,0<=j<=9,加之转移上一位也有[0,9],所以一个数求得结果只有10*10*2,非常快
转移:
首先判断i是奇数位还是偶数位
如果是奇数位;
if(j>=k)//表示第i位比第i-1位的值大,满足条件
dp[i][j][0] += dp[i-1][k][0];
if(k== num[i-1])//表示k是上一位的上界值
dp[i][j][0]+=dp[i-1][k][1];
if(j==num[i])
dp[i][j][1]+=dp[i-1][k][1];
如果是偶数位,那就判断j<=k时,下面不变。
最后答案就是 dp[最终位][][]的所有j 0/1 加和
代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int dp[20][10][2];//i表示在第i位,j表示i为是几,0表示未封顶,1表示封顶
int num[20];
int len;
int find_dp(int n)
{
len=0;
memset(dp,0,sizeof(dp));
int i,j,k,temp=n;
i=0;
while (temp>0)
{
num[++i]=temp%10;
temp/=10;
}
len=i;
for ( j = 0; j <num[len] ; j++)
{
dp[len][j][0]=1;
}
int ans=0;
dp[len][j][1]=1;;
for ( i = len-1; i >=1; i--)
{
for ( j = 0; j <= 9 ; j++)
{
for ( k = 0; k <= 9 ; k++)
{
if ((len-i)%2==1 && j>=k)
{
dp[i][j][0]+=dp[i+1][k][0];
if(j==num[i] && k==num[i+1])
dp[i][j][1]+=dp[i+1][k][1];
if (j<num[i])
dp[i][j][0]+=dp[i+1][k][1];
}
else if((len-i)%2==0 && j<=k)
{
dp[i][j][0]+=dp[i+1][k][0];
if(j==num[i] && k==num[i+1])
dp[i][j][1]+=dp[i+1][k][1];
if(j<num[i])
dp[i][j][0]+=dp[i+1][k][1];
}
}
}
}
for ( i = 0; i <= 9; i++)
{
ans+=(dp[1][i][0]+dp[1][i][1]);
}
return ans;
}
int main()
{
int t,n,m,a,b;
int ans;
int flag;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
int temp=n,len;
int i=0;
flag=1;
while (temp>0)
{
num[++i]=temp%10;
temp/=10;
}
len=i;
for ( i = len; i >=1 ; i--)
{
if((len-i)%2==0 && i<len && num[i] > num[i+1])flag=0;
if((len-i)%2==1 && num[i] <num[i+1])flag=0;
}
printf("%d\n",find_dp(m)-find_dp(n)+flag);
}
return 0;
}