light oj 1205(数位DP)
题目描述:
求给定区间中的回文数有多少个?
首先明确一点,如果一个数是回文数,那么给这个数两边加上相同的数,那么这个数还是回文数。
根据这点就可以进行递推了,p[start][end]=9*p[start+1][end-1](start位不为0)+p[start-1][end](start位为0);
在设计dfs的时候,由于回文数是对称的,所以只需要一个变量cur(cur>mid)就可以表示从cur到cur对称的位置的回文数的个数;
d[start][cur]表示从start位到cur位时,回文数的个数。
这句话隐含的意思是最高位为start,枚举到第cur位,由于是回文数,所以当cur>mid时,[cur,start]位确定,他们的对称位[1,start+1-cur]也就确定了,
还需枚举[cur,mid]这些位;当cur=mid时任意枚举,对回文数没有影响,当cur<mid时,由于是回文数,已经确定了,不需枚举。
再增加一维表示当前枚举的数字是不是回文数(([start,cur]位是否为回文数);
d[start][cur][state]表示从start位到cur位时,状态为state时回文数的个数。
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define LL long long LL n,m; LL d[30][30][2]; int digit[30]; int num[30]; LL dfs(int start,int cur,int Zero,int state,bool fp) { if(!cur) return state; if(!fp && d[start][cur][state]!= -1) return d[start][cur][state]; LL ret = 0 ;int fpmax = fp ? digit[cur] : 9; for(int i=0;i<=fpmax;i++) { if( (Zero &&(i==0)) ) ret+=dfs(start-1,cur-1,Zero&&(i==0),state, fp&& i==fpmax); else { num[cur]=i; if( (start & 1)==1 ) { int mid=((start+1)>>1); if(cur== mid) { ret+=dfs(start,cur-1,0,state,fp&& i==fpmax); } else if(cur < mid ) { ret+=dfs(start,cur-1,0,state&& (num[mid*2-cur]==i),fp&& i==fpmax); } else if(cur>mid) { ret+=dfs(start,cur-1,0,state,fp&& i==fpmax); } } else { int mid=(start>>1)+1; if(cur<mid) { ret+=dfs(start,cur-1,0,state&& (num[start+1-cur]==i),fp&& i==fpmax); } else { ret+=dfs(start,cur-1,0,state,fp&& i==fpmax); } } } } if(!fp) //如果是前缀,当前得到的ret的值,就不能代表dp[len][state]的值 d[start][cur][state] = ret; return ret; } LL f(LL n) { int len = 0; if(n==-1) return 0; while(n) { digit[++len] = n % 10; n /= 10; } LL answer=dfs(len,len,1,1,true); return answer; } void init() { memset(d,-1,sizeof(d)); } int main() { // freopen("test.txt","r",stdin); int t; scanf("%d",&t); int Case=0; while(t--) { scanf("%lld%lld",&n,&m); init(); if(n>m) swap(n,m); printf("Case %d: %lld\n",++Case,f(m)-f(n-1)); } return 0; }