【C++】回文数专项练习

如果一个数从左边读和从右边读都是同一个数, 就称为回文数
解答:

思想最简单的方法就是模拟,把各个数位取出来,然后比较。当然这种方法比较麻烦。
我们这次要用的方法如下:既然是回文数,那我们把各个数位上的数 正着 加起来跟 逆着 加起来的 和 是一样的。运用这条性质我们就可以很简单的解决这个问题。代码如下:

#include <iostream>
using namespace std;
 
int main(){
 
	int n;
    cin>>n;
    int x = 0;
	int s = n;    // 拷贝一份
	while(s>0){
		x = x*10+s%10;    //反向求和,s%10为取s的个位数,例,123取3
		s = s/10;        // 删除s的个位数,例,123变成12
	}
 
	if(x==n){
		cout<<"YES!"<<endl;
	}
	else{
		cout<<"NO!"<<endl;
	}
		return 0;
}

定义
  记字符串w 的倒置为wR
例如:
(abcd)R=dcba

(abba)R=abba(abba)

对字符串x,如果x 满足 xR=xx
则称之为回文;例如abba是一个回文,而abed不是。

一、判断回文数
法一
思路:
  将原数字倒序,比较是否还和原数相同。

特点:
此方法只适用于判断回文数,不能判断回文字符串。
此方法可以去除回文数可能存在的前导0再进行判断。

#include <iostream>
using namespace std;
int main() {
	int n, t, s = 0;
	cin >> n;
	t = n;			//拷贝一份n
	while (t) {		//将t倒序存入s中,注意此方法会去除前导0
		s = 10 * s + t % 10;
		t /= 10;
	}
	s == n ? cout << "yes" : cout << "no";
	return 0;
}

法二
思路:
  用字符串读入,双指针判断对称位置字符是否相同。

特点:
此方法适用于判断回文字符串。
此方法不可去除数字可能存在的前导0。

#include <iostream>
using namespace std;
int main() {
	string s;
	cin >> s;
	int i = 0, j = s.size() - 1;
	bool ok = true;				//标记是否为回文串
	while (i <= j) {
		if (s[i] != s[j]) {
			ok = false;
			break;
		}
		i++, j--;				//更新指针
	}
	ok ? cout << "yes" : cout << "no";
	return 0;
}

法三
思路:
  利用string读入,使用库中的翻转函数reverse()将字符串直接翻转,进行比较。

特点:
  代码简洁,可读性高,不易出错。

#include <iostream>
#include <algorithm>
using namespace std;
int main() {
	string s, t;
	cin >> s;
	t = s;
	reverse(t.begin(), t.end());
	s == t ? cout << "yes" : cout << "no";
	return 0;
}

1.回文数个数
思路:
  为了表述的简洁性,这里我们定义一个函数 c n t ( n ) ,表示 n 位数的回文数总个数。再定义一个函数 n u m ( n ) ,表示 n 位数的总个数。

根据回文数的定义,很显然任何 1 位数都是回文数,即 c n t ( 1 ) = 10。

回文数具有对称性,即关于中间的哪个数字镜面对称。那么我们就可以通过枚举左边一半的数字来构造一个回文数字。例如:左边一半是12,我们可以构造一个 3 位回文数121,我们也可以构造一个 4 位回文数1221。很显然,任意一个2位数字都可以构造成一个三位回文数和一个四位回文数,而任意一个三位回文数或四位回文数也唯一对应着一种构造方式。那么既然具有这种对应的关系,我们就可以确认:c n t ( 3 ) = c n t ( 4 ) = n u m ( 2 ) 。

将以上方法进行拓展,我们就可以将一个回文数与一个自然数取得对应关系。也就是说,任意一个回文数我们都能找到它的唯一构造方式。

例如:12321是由123构造得来,99是由9构造得来,1001是由10构造得来…

顺理成章地,我们得到如下关系:
对于 ∀ n ∈ N ∗ ,均满足 c n t ( 2 n ) = c n t ( 2 n − 1 ) = n u m ( n )

这样一来,我们把一个相对复杂的问题转化成了一个较简单的问题,即求 n u m ( n ) 。这是数学上一种重要的思想:化归思想。

一个 n 位数的个数很容易得到,即 10n - 10 n-1 ,也就是 9 X 10n-1
  综上,我们得到了这个问题的答案:对于 ∀ n ∈ N ∗ ,均满足 c n t ( 2 n ) = c n t ( 2 n − 1 ) = 9X10n-1
.

#include <iostream>
#include <cmath>
using namespace std;
int main() {
	int n;				//输入位数 n
	cin >> n;
	if (n & 1)	n++;	//如果n是奇数
	n >>= 1;			//无论奇偶都要除以2
	cout << 9 * pow(10, n - 1);
	return 0;
}
  1. 回文素数

描述
一个数如果从左往右读和从右往左读数字是相同的,则称这个数是回文数,如121,1221,15651都是回文数。给定位数n,找出所有既是回文数又是素数的n位十进制数。(注:不考虑超过整型数范围的情况)。
输入:位数 n ,其中1<=n<=9。
输出:
第一行输出满足条件的素数个数。
第二行按照从小到大的顺序输出所有满足条件的素数,两个数之间用一个空格区分。

样例输入:
1
样例输出:
4 2 3 5 7
思路:
  在这里我们还是分位数来讨论。

一位数都是回文数,那么显然只要满足质数即可。一位回文素数有 4 个:2 3 5 7.

两位数的回文数有9个,我们也很容易能看出来,除了11是质数外,其余的都是11的倍数,显然是合数。即:两位回文数有 1 个:11.

受此启发:我们不禁产生疑问,回文数具有如此优美的对称性,11的倍数也有一定的对称性,它们之间有没有什么联系呢?

答案是有的。

首先, 我们要有一定的前置知识,这属于小学奥数的范畴:如何判断一个数字是否为11的倍数:将该数字的奇数位相加的和与偶数位的和做差,差是11的倍数,则原数是11的倍数,否则不是。

例如:
  1234, ( 1 + 3 ) − ( 2 + 4 ) = − 2 ,不是11的倍数,故1234不是11的倍数。

1089:( 1 + 8 ) − ( 0 + 9 ) = 0 ,是11的倍数,故1089是11的倍数。

1221:( 1 + 2 ) − ( 2 + 1 ) = 0,是11的倍数,故1221是11的倍数。

于是我们发现:除11外,任意偶数位的回文数,均为11的倍数,即合数。 理由如下:

任意偶数位回文数形式为:a b c d . . . x x . . . d c b a ,其奇数位的和为 a + c + . . . + x + . . . + d + b ,偶数位的和为 b + d + . . . + x + . . . + c + a ,二者均等同于 a + b + c + d + . . . + x ,故差一定为0,即此回文数为11的倍数,为合数。

由此,我们探究出了1位和偶数位的回文数与素数的关系。至于其他位数的回文数与素数的关系,我们就只能逐个判断了。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
void print(int x) {
	if (x == 1) {
		cout << 4 << endl << "2 3 5 7";
		return;
	}
	if (x == 2) {
		cout << 1 << endl << 11;
		return;
	}
	if (!(x & 1)) {
		cout << 0 << endl;
		return;
	}
	int i, j;
	vector<int> a;						//保存素数
	int l = pow(10, x / 2);
	int r = pow(10, x / 2 + 1);
	for (i = l; i < r; i++) {			//枚举所有ceil(x/2)位数
		if (!((i / l) & 1))				//小优化,如果最高位是偶数,即构造出来的回文数
			i += l;						//是偶数,则其一定不是质数,那么直接跳过这些数
		int t = i, p = i / 10;
		while (p) {						//构造其对应的回文数t
			t = 10 * t + p % 10;
			p /= 10;
		}
		bool ok = true;
		for (j = 2; j <= t / j; j++) {	//简单的判断素数
			if (t % j == 0) {
				ok = false;
				break;
			}
		}
		if (ok)
			a.push_back(t);
	}
	cout << a.size() << endl;
	for (auto it = a.begin(); it != a.end(); it++)
		cout << *it << " ";
}
int main() {
	int n;
	cin >> n;
	print(n);
	return 0;
}

练习题一:
回文数的定义为:如果把一个数的各个数位上的数字颠倒过来得到的新数与原数相等,则此数是回文数,例:7,22,131,2112,31013,…都是回文数。 对任意给出的一个整数n,经过一系列的处理,最后都能成为回文数。处理的方法是,该数加上它的颠倒数,
例如:n=176
第一次处理后    176+671=847
第二次处理后    847+748=1595
第三次处理后    1595+5951=7546
第四次处理后    7546+6457=14003
第五次处理后    14003+30041=44044
此时成为回文数,共进行5次处理。
问题:给出n 后,求出使该数按照以上规则进行一系列处理后成为回文数的最少操作次数。

输入数据
n 一个整数(n 为int范围内数据,并保证计算过程产生的最大数据范围不会超出long long范围)

输出数据
使n成为回文数的最少处理次数。 若开始给出的n是回文数,则输出0(即不需任何处理)。

# include <bits/stdc++.h>
using namespace std;
long long n,num=0;
long long fan(long long h)
{
	long long i=0;
	while (h!=0){
	    i=i*10+h%10;
	    h/=10;
	}
	return i;
}
void hw(long long x)
{
    if (x==fan(x)){
        cout<<num;
        return ;
    }
    else{
        num+=1;
        hw(x+fan(x));
    }
}
int main(){
    cin>>n;
    hw(n);
    return 0;
}

练习题二:
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月 份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现 在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存 在的日期是回文的。

一个8位数字是回文的,当且仅当对于所有的i ( 1 <=i<= 8 )从左向右数的第i个 数字和第9-i个数字(即从右向左数的第i个数字)是相同的。

例如:

•对于2016年11月19日,用8位数字20161119表示,它不是回文的。

•对于2010年1月2日,用8位数字20100102表示,它是回文的。

•对于2010年10月2日,用8位数字20101002表示,它不是回文的。

每一年中都有12个月份:

其中,1、3、5、7、8、10、12月每个月有31天;4、6、9、11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

1.这个年份是4的整数倍,但不是100的整数倍;

2.这个年份是400的整数倍。

例如:

•以下几个年份都是闰年:2000、2012、2016。

•以下几个年份是平年:1900、2011、2014。

输入格式:

输入包括两行,每行包括一个8位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证date1和date2都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。

保证date1一定不晚于date2。

输出格式:
输出一行,包含一个整数,表示在date1和date2之间,有多少个日期是回文的。

提示:对于60%的数据,满足date1 = date2。

样例:

输入:
20110101
20111231
输出:
1
c++

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值