A palindromic number or numeral palindrome is a 'symmetrical' number like 16461 that remains the same when its digits are reversed. In this problem you will be given two integers i j, you have to find the number of palindromic numbers between i and j (inclusive).
Input starts with an integer T (≤ 200), denoting the number of test cases.
Each case starts with a line containing two integers i j (0 ≤ i, j ≤ 1017).
Output
For each case, print the case number and the total number of palindromic numbers between i and j (inclusive).
Sample Input
4
1 10
100 1
1 1000
1 10000
Sample Output
Case 1: 9
Case 2: 18
Case 3: 108
Case 4: 198
虽然说是数位dp,但是我的做法却是不伦不类的,但是自己感觉还是蛮有趣的,就在这里记录一下吧;
题目大意:
给你i,j求两个数之间回文数的个数,注意前导零;
思路:
一.既然是求i,j之间的个数,还是老套的求f(j)-f(i-1);
二.利用dp[i]表示所有i位的数有多少回文数(例如i=3时,保存100到999之间的回文数),这也是我用到的全部数位dp了;
那么要如何保存呢,分奇数位和偶数位两种情况:
偶数位如果保证回文,要求对称,如果是i位数,前i/2位要对称于后i/2位,
例如四位数,前2位数字有10到99共有90种情况,所以四位数共有90个回文数;
奇数位相当于在少一位的偶数位中间插入一个数,这个数可以是0到9共10个数任意一个;
例如五位数,中间不考虑就是有90种情况,这时考虑中间数有10种,所以共有90*10种情况;
得到递推公式:
if(i%2)
dp[i]=dp[i-1]*10;
else
dp[i]=num[i/2];
ps:int num[20]={0,9,90,900,9000,90000,900000,9000000,90000000};
因为num知道,所以直接写了个数组存;
注意0也是回文数,所以dp[1]=10;
三.分步求结果,感觉更像数学公式,这里拿34512这个数字举例子:
1.利用递推公式可知0~9999共有dp[1]+dp[2]+dp[3]+dp[4]个;
2.开始计算10000~34512;
(1)先拆分34512为34 5 12;
(2)则前两位为10~33时共33-9种情况时共有(33-9)*10个回文数,此时中间数可以为0~9,后两位可以是任何组合;
(3)前两位为34时,考虑中间数,有0~4共5种情况,此时后两位可以是任何组合;
(4)前两位为34,中间为5时,比较最后两位与前两位的对称数,因为此时只有34543是回文数,但是12比43小,达不到43,那么这种情况就不算;
3.于是最后结果就是dp[1]+dp[2]+dp[3]+dp[4]+(33-9)*10+5+0,四步合一得到最终结果;
4.偶数位同理,但是不考虑中间数罢了;
代码(代码很杂乱,思路也不算清晰,写到哪里想到哪里,不打推荐看代码,容易绕晕,但是只要掌握上面方法就很容易弄清了):
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
int num[20]={0,9,90,900,9000,90000,900000,9000000,90000000};
int first0[20]={0,0,9,99,999,9999,99999,999999,9999999}; //多余的含前导零数
LL dp[20];
struct huiwen
{
int l,fl,mid,r,flag,len;//flag=0为偶数 =1为奇数 fl存储的是前几个数的对称数
}h;
LL solve()
{
LL i,sum=0;
for(i=1;i<h.len;i++)
{
sum+=dp[i];
}
if(h.flag)
{
sum+=((h.l-1-first0[h.len/2])*10+h.mid);
if(h.fl<=h.r)
sum++;
}
else
{
sum+=(h.l-1-first0[h.len/2]);
if(h.fl<=h.r)
sum++;
}
return sum;
}
int putin(LL x)
{
int a[20],i=0,m;
while(x)
{
a[++i]=x%10;
x/=10;
}
m=(i+1)/2;
h.l=h.r=h.fl=0;
h.len=i;
if(i%2)
{
h.flag=1;
h.mid=a[m];
for(;i>m;i--)
h.l=(h.l*10+a[i]);
for(i=m+1;i<=h.len;i++)
h.fl=(h.fl*10+a[i]);
for(i=m-1;i>=1;i--)
h.r=(h.r*10+a[i]);
}
else
{
h.flag=0;
for(;i>m;i--)
h.l=(h.l*10+a[i]);
for(i=m+1;i<=h.len;i++)
h.fl=(h.fl*10+a[i]);
for(i=m;i>=1;i--)
h.r=(h.r*10+a[i]);
}
return 0;
}
int main()
{
int i,t,ca=1;
LL a,b;
dp[1]=10;
for(i=2;i<=17;i++)
{
if(i%2)
dp[i]=dp[i-1]*10;
else
dp[i]=num[i/2];
}
cin>>t;
while(t--)
{
cin>>a>>b;
if(a>b)
swap(a,b);
if(b<10)
b++;
else
{
putin(b);
b=solve();
}
if(a>10)
{
putin(a-1);
a=solve();
}
cout<<"Case "<<ca++<<": "<<b-a<<endl;
}
}
看了网上大触的做法,感觉我这个比起来要繁琐很多,但是比较容易想明白,偏低思考的做法Orz。