NOJ1046第K回文数——???

本文介绍了如何找到第K个回文数的方法,通过分析回文数的规律,确定其位数和左右半边,对于给定的K值,可以计算出对应的回文数。例如,当K=224时,第224个回文数是12521。文章还讨论了从回文数逆向求解其排列位置的思路。

第K回文数

Time Limit(Common/Java):1000MS/3000MS          Memory Limit:65536KByte
Total Submit:612            Accepted:150

Description

回文数是这样一个正整数:它从左往右读和从右往左读是一样的。例如1111121505都是回文数。将1100,000,000内所有回文数按从小到达排序后,第k个回文数是多少呢?

Input

第一行为一个整数N,表示询问的次数。以下N行每行一个整数k,表示询问第k个回文数是多少。

Output

输出共N行,按照输入数据的顺序,依次输出第k个回文数。

Sample Input

2
5
10

Sample Output

5
11

Source

NUAA


分析:找规律。

1位数中回文数有9个:1,2,3,4,5,6,7,8,9

2位数中回文数有9个:11,22,33,44,55,66,77,88,99

3位数中回文数有90个:101,111,121,131,141,151,161,171,181,191;202,212,222。。。;909,919,929,939,949,959,969,979,989,999。

4位数中回文数有90个

。。。

相邻的位数中的回文数个数相同,且是10倍递增的。其次,同一位数里面的回文数的增长也是有规律的。如:101,111,121,131。。。


如果直接求出回文数存在数组里。莫名其妙的Wrong Answer!

下面给出比较好的写法,运行时间为0MS:

如k=224时,第K回文数为12521。先求出第K回文数的位数,再求出该数的左半边,因为对称,再求出右半边。(注意位数为奇数的情况)。最终的结果即为答案。


有意思的是:k减去每个位数的回文数的时候,如k-9-90-90-900-900。。。当k继续减位数时,k<0 那么就能确定第K回文数的个数,同时,这个回文数的左半边的除最高位之外的数也确定了。举例:

如k=224时,k-10-10-90-90>0,再减去900就小于0了,所以确定第224个回文数是5位数。同时,这个数的左半边的位数也能确定为3((5+1)/2)。且这个数的左半边是从100(因为左半边为3位)开始的第(k-10-10-90-90-1)个数。

所以这个数的左半边能确定了,实际即为100+25=125。

接下去的任务就是对称把右半边求出来。再整合即得到答案ans。这边的求右半边的数的写法比较nice!


反过来,如果给定12521,求它是第几个回文数,解法应该为:

解:因为12521为5位数,所以在它之前有9+9+90+90个数。12521在5位数中是10+10+6个。(0 1 2 3 4 5)

所以12521为第(9+9+90+90+10+10+6)= 224 个。

#include<stdio.h>

//第K回文数

void fun(int k)
{
	int half_len, left, weishu, num, ans = 0;
	num = 9; // 几位数对应多少回文数 1-9,2-9,3-90,4-90...
	for(weishu=1;k-num>0;weishu++) // 有几位,1开始 求出第k个回文数的位数
	{
		// 对应的回文数 > k
		k -= num;
		if(weishu % 2 == 0) // 1位-9个,2位-9个,3-90,4-90...每2个增量为10倍
			num *= 10;
	}
	half_len = (weishu+1) / 2; // 回文数的位数的一半,如12521为3
	left = 1; // 回文数的左边一半,如12521中的125

	for(int i=2;i<=half_len;i++)
		left *= 10;
	left += k-1; // 125

	ans += left; // 125
	if(weishu % 2 != 0) // 位数为奇数,左半边比右半边多一位
		left /= 10; // 12
	while(left)
	{
		ans = ans*10 + left%10; // (125*10+2)*10+1
		left /= 10;
	}
	printf("%d\n",ans);
}

int main()
{
	int n, k;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&k);
		fun(k);
	}
	return 0;
}

还有一种比较清晰但是略烦的写法:

#include<stdio.h>
int main()
{
	int n,j,i,k;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d",&k);
		if(k <= 9)
			;
		else if(k <= 18)
			k=(k-10)*11+11;
		else if(k <= 108)
			k=((k-19)/10+1)*101+(k-19)%10*10;
		else if(k <= 198)
			k=((k-109)/10+1)*1001+(k-109)%10*110;
		else if(k <= 1098)
			k=((k-199)/100+1)*10001+(k-199)/10%10*1010+(k-199)%10*100;
		else if(k <= 1998)
			k=((k-1099)/100+1)*100001+(k-1099)/10%10*10010+(k-1099)%10*1100;
		else if(k <= 10998)
			k=((k-1999)/1000+1)*1000001+(k-1999)/100%10*100010+(k-1999)%10*1000+(k-1999)/10%10*10100;
		else if(k <= 19998)
			k=((k-10999)/1000+1)*10000001+(k-10999)/100%10*1000010+(k-10999)/10%10*100100+(k-10999)%10*11000;
		printf("%d\n",k);
	}
}

最后一种写法,一开始就把所有的回文数求出来,按从小到大排好序。但是提交Wrong Answer!???
#include<iostream>
#include<algorithm>
using namespace std;

int cmp(const void *a,const void *b)
{
    return *(int *)a-*(int *)b;
}


int a[10000000];

int main()
{
    int n, m, i = 0, d1, d2, d3, d4, d5;
    for(d1 = 1; d1 <= 9; d1++)
    {
        a[i++] = d1;
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        a[i++] = d1*10+d1*1;
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            a[i++] = d1*100+d2*10+d1*1;
        }
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            a[i++] = d1*1000+d2*100+d2*10+d1*1; 
        }
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            for(d3 = 0; d3 <= 9; d3++)
            {
                a[i++]= d1*10000+d2*1000+d3*100+d2*10+d1*1;
            }
        }
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            for(d3 = 0; d3 <= 9; d3++)
            {
                a[i++] = d1*100000+d2*10000+d3*1000+d3*100+d2*10+d1*1;
            }
        }
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            for(d3 = 0; d3 <= 9; d3++)
            {
                for(d4 = 0; d4 <= 9; d4++)
                {
                    a[i++] = d1*1000000+d2*100000+d3*19999+d4*1000+d3*100+d2*10+d1*1;
                }
            }
        }
    }

    for(d1 = 1; d1 <= 9; d1++)
    {
        for(d2 = 0; d2 <= 9; d2++)
        {
            for(d3 = 0; d3 <= 9; d3++)
            {
                for(d4 = 0; d4 <= 9; d4++)
                {
                    a[i++] = d1*10000000+d2*1000000+d3*100000+d4*10000+d4*1000+d3*100+d2*10+d1*1;
                }
            }
        }
    }

    qsort(a,i,sizeof(int),cmp);

    scanf("%d", &n);
    while(n--)
    {
        scanf("%d", &m);
        printf("%d\n",a[m-1]);
    }
    return 0;
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值