题目描述

输入一个t, 表示有t组样例,每组样例输入一个整数n,求区间 [1,n]内有多少个回文数。

解题思路

按照常规的从1枚举到n再依次判断每个数是否为回文数显然是行不通的,所以我们需要用数学方法。

首先考虑位数为偶数的时候,如1542,位数为4,将它对半分为15和42,则当前半部分为14的时候,后两位可以取00到99之间的任意数,所以前两位从10到14都可以找到对应的后两位(如01,41)来形成回文数。此时我们只需考虑前两位是15的情况。观察知道后两位至少得是51才能与15形成回文数,而42<51,显然不行,也就是说1542只可形成前两位10到14这样5个回文数(1001,1111,1221,1331,1441)。求出了1000以上的回文数有多少个,再加上小于1000的回文数个数,即是[1,1542]之间回文数的个数。

故:对于数abcd,大于1000时,我们只需比较cd与ba的大小,如果cd>=ba,则有(cd-10)+1个回文数,否则为cd-10个。对于任意有2*l位数,令t=a/(10^l)(t就是该数的前面一半),则:

ans=f[2*l-1]+t-10^(l-1)或ans=f[2*l-1]+t-10^(l-1)+1;

同理,对于位数为奇数的数如25368,只需比较368与352的大小,答案情况同上,如abcde,比较cde与cba的大小。如果数很大的话,最好用long long,这里用的是int.

如果要是求区间[a, b]内的回文数,只需分别求出[1, b]和[1, a]内的回文数,两者相减即可得出答案。

#include <stdio.h>
int f[12] = {0, 9, 18, 108, 198, 1098, 1998, 10998, 19998, 109998, 199998, 1099998};
int find(int x)
{
    int l, i, ans, t, a, b, na, nx;
    if (x < 10)
        return x;
    l = 0;
    nx = x;
    while (nx)
    {
        l++;
        nx /= 10;
    }
    t = 1;
    ans = f[l - 1];
    for (int k = 1; k <= l / 2; k++)
        t *= 10;
    na = 0;
    a = x / t;
    if (l & 1)
        t *= 10;
    b = x % t;
    t /= 10;
    ans += a - t;
    while (a)
    {
        na = na * 10 + a % 10;
        a /= 10;
    }
    if (b >= na)
        ans++;
    return ans;
}
int main()
{
    int t, n;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        printf("%d\n", find(n));
    }
    return 0;
}