数位DP
用记忆化搜索来实现
dfs设五个参数 pos,len,pre,sta,limit;
pos为当前枚举到第几位,从高往低枚举。
pos | 为当前枚举到第几位,从高往低枚举。 |
len | 有没有前导零,0代表有,1代表没有 |
pre | 当前位的上一位 |
sta | 设的状态,这题有10个,为0~9分别代表当前位上是几 |
limit | 高位标记 |
设一个 f 数组
为位数为pos,上一位为pre,当前状态为sta时的答案
枚举每一位上的数字(0~9)
如果有高位标记,那么最多枚举到a[pos];
a[i]表示这个数的第i位是多少
那么很显然当abs(pre-i)<2就得跳过这个数
上代码
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int f[100][10][100],a[100];
int dfs(int pos,int len,int sta,int pre,bool limit)
{
if (pos==0) return 1;
if (!limit&&f[pos][sta][pre]!=-1) return f[pos][sta][pre];
int up=limit?a[pos]:9;//判断高位标记
int tmp=0;
for (int i=0;i<=up;i++)
{
if (pre!=-1&&abs(pre-i)<2) continue;//如果不合法就跳过
if (i==0&&len==0) tmp+=dfs(pos-1,0,0,-1,limit&&i==a[pos]);
else
{
tmp+=dfs(pos-1,1,i,i,limit&&i==a[pos]);
}
}
if(!limit) f[pos][sta][pre]=tmp;
return tmp;
}
int solve(int x)
{
int cnt=0;
while (x!=0)
{
a[++cnt]=x%10;
x=x/10;
}
dfs(cnt,0,0,-1,true);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(f,-1,sizeof(f));
printf("%d\n",solve(m)-solve(n-1));
}