bzoj3134 [Baltic2013]numbers-数位dp

题意:

一个数是非回文数当且仅当不包含长度大于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
#include
#include
#include
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);
} 

你可能感兴趣的:(数位DP)