题意:
一个数是非回文数当且仅当不包含长度大于1的回文数。比如16276是无回文数,而17276因为含有727而不是。
求区间内有多少个非回文数。
Solution:
非常经典的数位dp问题,一开始想到f[len][pre][pre2][0/1]表示第len位,第len-1位是pre,第len-2位是pre2,是否到达上界的方案数,但是发现这种状态下前导零会影响答案,那么我们多存储两个状态:f[len][pre][pre2][zero1][zero2][0/1]表示第len位,第len-1位是pre,第len-2位是pre2,第len-1位是否是前导零,第len-2位是否是前导零,是否到达上界的方案数,
记忆化搜索即可。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
long long ans;
long long f[20][10][10][2][2][2];
long long n;
int s[20];
int len=0;
long long dp(int l,int pre,int pre2,bool zero1,bool zero2,bool edg)
{
if (l>len) return 1;
if (f[l][pre][pre2][zero1][zero2][edg]) return f[l][pre][pre2][zero1][zero2][edg];
long long sum=0;
if (edg)
{
for (int i=0;i<=s[l];i++)
if ((zero1||i!=pre)&&(zero2||i!=pre2))sum+=dp(l+1,i,pre,zero1&&(i==0),zero1,edg&&i==s[l]);
f[l][pre][pre2][zero1][zero2][edg]=sum;
return sum;
}
else
{
for (int i=0;i<=9;i++)
if ((zero1||i!=pre)&&(zero2||i!=pre2))sum+=dp(l+1,i,pre,zero1&&(i==0),zero1,0);
f[l][pre][pre2][zero1][zero2][edg]=sum;
return sum;
}
}
long long solve(long long n)
{
memset(f,0,sizeof(f));
if (n<0) return 0;
len=0;
while (n)
{
len++;
s[len]=n%10;
n/=10;
}
reverse(s+1,s+1+len);
return dp(1,0,0,1,1,1);
}
int main()
{
scanf("%lld",&n);
n--;
ans-=solve(n);
scanf("%lld",&n);
ans+=solve(n);
printf("%lld",ans);
}