[数位DP][记忆化搜索]JZOJ 3316 非回文数字
分析
带限制求两数中总数,较显然是数位DP
我们考虑,如果第i位与其i-1位和i-2位都不相同,那么这个数是非回文子树
那么我们设f[i][j][k][l]表示做到第i位,i-2位为j,i-1位为k,是否在边界上
但是我们必须注意特殊的状况:前导零
那么我们设多两位状态[zero2][zero1]表示i-2位是否前导零,i-1位是否前导零,对于前导零不需要进行回文的判断
数位DP的递推好恶心啊
记忆化搜索即可
#include <iostream> #include <cstdio> #include <memory.h> using namespace std; typedef long long ll; ll a,ans; ll f[20][10][10][2][2][2]; int c[20],len; ll Calc(int dep,int pre2,int pre1,bool zero2,bool zero1,bool limit) { if (f[dep][pre2][pre1][zero2][zero1][limit]>=0) return f[dep][pre2][pre1][zero2][zero1][limit]; ll ans=0; if (dep>len) return 1; if (limit) { for (int i=0;i<=c[dep];i++) if ((zero2||i!=pre2)&&(zero1||i!=pre1)) ans+=Calc(dep+1,pre1,i,zero1,zero1&&(i==0),i==c[dep]); } else { for (int i=0;i<=9;i++) if ((zero2||i!=pre2)&&(zero1||i!=pre1)) ans+=Calc(dep+1,pre1,i,zero1,zero1&&(i==0),0); } return f[dep][pre2][pre1][zero2][zero1][limit]=ans; } ll Solve(ll x) { if (x<0) return 0; len=0;memset(f,-1,sizeof f); while (x) c[++len]=x%10,x/=10; for (int i=1;i<=len/2;i++) swap(c[i],c[len-i+1]); return Calc(1,0,0,1,1,1); } int main() { scanf("%lld",&a);a--; ans-=Solve(a); scanf("%lld",&a); ans+=Solve(a); printf("%lld",ans); }
在日渐沉没的世界里,我发现了你。