Peter 回文数

这篇博客主要讨论了如何寻找比给定数字大的所有回文数中第k小的数。作者提供了两种方法:一是暴力枚举所有回文数并使用upper_bound查找,二是通过观察回文数的规律进行打表和二分查找。样例代码展示了这两种方法的实现,并给出了具体的测试样例。
摘要由CSDN通过智能技术生成

回文数

描述

回文数字是指一个数字是左右对称的。比如1221,121这种,左右两边的数字反转之后依旧保持一样的数字叫做回文数字。Peter有一个幸运数字是6,他现在想要知道对于某一个数字,比这个数字大的所有回文数字当中,第6小的回文数字是多少。由于Peter的话很多,所以他可能会问很多次。

输入

首先,输入一个数字q,表示Peter询问的次数。(1<=q<=1e4)

然后,对于每一次询问,输入一个数字x,表示询问的数字。(0<=x<=1e9)

输出

对于每一次询问,你需要输出比给定的数字的大的所有回文数字里面,第6小的回文数字是多少。

输入样例 1 

2

1

11

输出样例 1

7

77

提示

样例解释,询问两次,第一次询问比1大的所有回文数字里面,第6小的数字,我们发现,在1后面的回文数字依次是2,3,4,5,6,7,8,9,11......,其中7是第6小的。所以答案就是7;第二次询问比11大的所有回文数字里面,第6小的数字,我们发现,在11后面的回文数字依次是22,33,44,55,66,77,88,99......,其中77是第6小的。所以答案就是77.

巧妙点:upper_bound()的使用

upper_bound() 算法会在前两个参数定义的范围内查找大于第三个参数的第一个元素。对于这两个算法,它们所查找的序列都必须是有序的,而且它们被假定是使用 < 运算符来排序的。eg.  int pos = upper_bound(a,a+cnt,x)-a;

upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

(1)暴力枚举:将所有数都存储起来,然后找时用upper-bound函数找出下标,再输出在它后面的第5个数字

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 2e6+10;

ll a[maxn],cnt=0;

int main(){

//1

for(int i=1;i<10;i++){

a[++cnt]=i;

}

//2

for(int i=1;i<10;i++){

a[++cnt]=i*10+i;

}

//3

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

a[++cnt]=i*100+j*10+i;

}}

//4

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

a[++cnt]=i*1000+j*100+j*10+i;

}

}

//5

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

a[++cnt]=i*10000+j*1000+k*100+j*10+i;

}

}

}

//6

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

a[++cnt]=i*100000+j*10000+k*1000+k*100+j*10+i;

}

}

}

//7

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

for(int m=0;m<10;m++){

a[++cnt]=i*1000000+j*100000+k*10000+m*1000+k*100+j*10+i;

}

}

}

}

//8

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

for(int m=0;m<10;m++){

a[++cnt]=i*10000000+j*1000000+k*100000+m*10000+m*1000+k*100+j*10+i;

}

}

}

}

//9

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

for(int m=0;m<10;m++){

for(int n=0;n<10;n++){

a[++cnt]=i*100000000+j*10000000+k*1000000+m*100000+n*10000+m*1000+k*100+j*10+i;

}

}

}

}

}

//10

for(int i=1;i<10;i++){

for(int j=0;j<10;j++){

for(int k=0;k<10;k++){

for(int m=0;m<10;m++){

for(int n=0;n<10;n++){

a[++cnt]=i*1000000000+j*100000000+k*10000000+m*1000000+n*100000+n*10000+m*1000+k*100+j*10+i;

}

}

}

}

}

int q;

cin>>q;

while(q--){

ll x;

cin>>x;

int pos = upper_bound(a,a+cnt,x)-a;

cout<<a[pos+5]<<endl;

}

return 0;

(2)找规律:打表,二分

首先,我们可以发现在 [0,1e9] 的回文数是固定的,所以我们可以打表求出在这个范围内的所有的回
文数。然后,我们发现,在这个范围内的回文数字其实是很少的。一位的有 10 个,两位的有 9 个,
三位的有 9x10 个,四位的有 9x10 ,五位的有 9x10x10 个,六位的有 9x10x10 个,七,八位的有
9x10x10x10 个,九,十位的有 9x10x10x10x10 个,加起来一共大概 1e5 个左右。然后,我知道的
打表的方法有两种,一种是暴力循环枚举每一位的数字(注意,只需要枚举一半就行 ). 另一种是使
dfs 简化代码。找到所有的回文数之后,可以使用二分进行查找,找到第一个大于给定的数字的
回文数的下标,向后找 5 个即可。这里可以使用 upper_bound 进行查找。
ans1.cpp
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std ;
typedef long long ll ;
ll a [ 1000010 ];
int cnt = 0 ;
// 处理类似于 1221 这样的回文数
void deal1 ( ll x , int now , int num ){
if ( num == now ){
ll tmp1 = x , tmp2 = 0 ; while ( x ){
tmp2 += x % 10 ;
x /= 10 ;
tmp2 *= 10 ;
}
tmp2 /= 10 ;
while ( num -- ){
tmp1 *= 10ll ;
}
tmp1 += tmp2 ;
a [ ++ cnt ] = tmp1 ;
return ;
}
for ( int i = 0 ; i < 10 ; i ++ ){
deal1 ( x * 10 + i , now + 1 , num );
}
}
// 处理类似于 121 这样的回文数
void deal2 ( ll x , int now , int num ){
if ( num == now ){
ll tmp1 = x , tmp2 = 0 ;
while ( x ){
tmp2 += x % 10 ;
x /= 10 ;
tmp2 *= 10 ;
}
tmp2 /= 10 ;
tmp1 *= 10 ;
for ( int i = 0 ; i < 10 ; i ++ ){
ll tmp3 = tmp1 + i ;
for ( int j = 1 ; j <= num ; j ++ ){
tmp3 *= 10 ;
}
tmp3 += tmp2 ;
a [ ++ cnt ] = tmp3 ;
}
return ;
}
for ( int i = 0 ; i < 10 ; i ++ ){
deal2 ( x * 10 + i , now + 1 , num );
}
}
int main (){
for ( int i = 0 ; i < 10 ; i ++ ){
a [ ++ cnt ] = i ;
}
for ( int i = 1 ; i <= 4 ; i ++ ){
for ( int j = 1 ; j < 10 ; j ++ ){ // 处理类似于 1221 这种的回文数
deal1 ( j , 1 , i );
}
for ( int j = 1 ; j < 10 ; j ++ ){ // 处理类似于 121 这种的回文数
deal2 ( j , 1 , i );
}
}
// 以上打表出来的结果最大到 999999999 1e9 之后的需要自己手动补充
a [ ++ cnt ] = 1000000001 ;
a [ ++ cnt ] = 1000110001 ;
a [ ++ cnt ] = 1000220001 ; a [ ++ cnt ] = 1000330001 ;
a [ ++ cnt ] = 1000440001 ;
a [ ++ cnt ] = 1000550001 ;
a [ ++ cnt ] = 1000660001 ;
a [ ++ cnt ] = 1000770001 ;
sort ( a + 1 , a + 1 + cnt );
int q ;
cin >> q ;
while ( q -- ){
ll n ;
cin >> n ;
int pos = upper_bound ( a + 1 , a + 1 + cnt , n ) - ( a + 1 ) + 6 ;
cout << a [ pos ] << endl ;
}
return 0 ;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值