题目如下:
给定一个 k+1 位的正整数 N,写成
a
k
.
.
.
a
1
a
0
a_k...a_1a_0
ak...a1a0的形式,其中对所有 i 有
0
≤
a
i
<
10
0≤a_i<10
0≤ai<10且
a
k
>
10
a_k>10
ak>10。N 被称为一个回文数,当且仅当对所有 i 有
a
i
=
a
k
−
i
a_i = a_{k-i}
ai=ak−i。零也被定义为一个回文数。非回文数也可以通过一系列操作变出回文数。首先将该数字逆转,再将逆转数与该数相加,如果和还不是一个回文数,就重复这个逆转再相加的操作,直到一个回文数出现。如果一个非回文数可以变出回文数,就称这个数为延迟的回文数。(定义翻译自 https://en.wikipedia.org/wiki/Palindromic_number )
给定任意一个正整数,本题要求你找到其变出的那个回文数。
输入格式:
输入在一行中给出一个不超过1000位的正整数。
输出格式:
对给定的整数,一行一行输出其变出回文数的过程。每行格式如下
A + B = C
其中 A 是原始的数字,B 是 A 的逆转数,C 是它们的和。A 从输入的整数开始。重复操作直到 C 在 10 步以内变成回文数,这时在一行中输出 C is a palindromic number.;或者如果 10 步都没能得到回文数,最后就在一行中输出 Not found in 10 iterations.。
输入样例 1:
97152
输出样例 1:
97152 + 25179 = 122331
122331 + 133221 = 255552
255552 is a palindromic number.
输入样例 2:
196
输出样例 2:
196 + 691 = 887
887 + 788 = 1675
1675 + 5761 = 7436
7436 + 6347 = 13783
13783 + 38731 = 52514
52514 + 41525 = 94039
94039 + 93049 = 187088
187088 + 880781 = 1067869
1067869 + 9687601 = 10755470
10755470 + 07455701 = 18211171
Not found in 10 iterations.
解决方案:
第一次做的时候最后一个测试样例没有通过,后来上网看大佬解释以后说最后一个测试用例是1000位的正整数相加,应该使用大整数高精度加法来计算,没办法,只好推倒原来的代码重写,正好最近刚学习了一些STL,因此我使用了一些STL来解决这个题,顺便巩固一下STL的东西。当然,这一版本的代码还是有些臃肿和复杂,留待以后再进行优化和重构。
1.思路:本题的思路其实并不复杂,按照题目描述:“首先将该数字逆转,再将逆转数与该数相加,如果和还不是一个回文数,就重复这个逆转再相加的操作,直到一个回文数出现。如果一个非回文数可以变出回文数,就称这个数为延迟的回文数。”这正好就是本题的思路;做一个递归函数,如果满足上述条件,就退出递归,如果超出十次,就给出相应的输出。而本题的重点和难点则在于如何实现大整数的逆转相加。只要解决这个问题,就可以解出本题。
2.大整数的存储:因为题目给的整数位数较大,所以使用string字符串来存储整数,当然,在计算的时候,还需要将string字符串转换为int型数组来进行大整数相加运算。因为不知道整数的位数,所以使用STL容器vector来存储string字符串转换来的整形数组。
这部分的代码为:
vector<int> a;
vector<int> b;
string aa = str;
for(int i = 0; i < aa.length(); i++) {
a.push_back(str[i] - '0');
}
reverse(str.begin(), str.end()); //STL中的reverse函数可以对字符串进行反转操作
string bb = str;
for(int i = 0; i < bb.length(); i++) {
b.push_back(str[i] - '0');
}
3.大整数的相加:大整数的相加还是从《算法笔记》上学的基本思路。在这里重新整理一下,这里就拿样例输入97152来举例:
我们平常用竖式计算的方法是这样的:每一位相加,遇到进位的时候就进位。而大整数相加的方法和竖式计算是一样的,数组的每一位相加,遇到进位的时候就进位,然后将每一位数字存储进数组中。
大整数相加在算法笔记中的代码是这样的:
//因为竖式计算是从后往前的计算,所以数组需要逆着赋值。也就是说97152赋值进来的时候应该是25179.
int carry = 0; //carry是进位
for(int i = 0; i < a.len || b.len; i++) {
int temp = a[i] + b[i] + carry; //两个对应位与进位相加
c[i++] = temp % 10; //个位数位该位的结果
carry = temp / 10; //十位数位新的进位
}
if(carry != 0) c[i++] = carry; //如果最后进位不为0,则直接赋值给结果的最高位。
/*可以手动执行一下代码来加固一下思路和记忆:
第0次循环:a[0] = 2,b[0] = 9,a[0] + b[0] + carry = 9 + 2 + 0 = 11
c[0] = 11 % 10 = 1 这是个位数的数字
carry = 11 / 10 = 1 这是进位
第1次循环:a[1] = 5,b[0] = 7,a[0] + b[0] + carry = 5 + 7 + 1 = 13 这已经加上了上一次的进位
c[1] = 13 % 10 = 3 这是十位数的数字
carry = 13 / 10 = 1 这是进位
后面以此类推。。。。。。
但是,在《算法笔记》中,为了方便竖式从后往前的计算,人家的数组是逆着赋值的。但是因为我没有逆着赋值,而且同样使用了算法笔记中的这种思路,那么如果同样使用竖式计算的话,结果就变成了从前往后的计算,就成了下面这个样子:
这明显是错误的,因为其正好成了正确数字的逆转。那么现在就需要一个容器,这个容器可以使先放进去的数后出来,很明显,应该使用容器-----栈。
所以,这部分的代码为:
stack<int> c;
int carry = 0;
int temp = 0;
for(int i = 0; i < max(aa.size(), bb.size()); i++) {
temp = a[i] + b[i] + carry;
c.push(temp % 10);
carry = temp / 10;
}
if(carry != 0) c.push(carry);
int size = c.size(); //防止c.size在弹出栈顶元素的时候减小
char m[2000];
for(int i = 0; i < size; i++) { //这里一定不能使用c.size(),因为栈顶元素会弹出使得c.size()减小
sprintf(&m[i], "%d", c.top()); //将数组转换为字符串
c.pop();
}
string cc = m;
完整代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
int calculate(string str, int times)
{
if(times > 9) {
cout << "Not found in 10 iterations." << endl;
return 0;
} //第十次就不行
vector<int> a;
vector<int> b;
stack<int> c;
string aa = str;
for(int i = 0; i < aa.length(); i++) {
a.push_back(str[i] - '0');
}
reverse(str.begin(), str.end());
string bb = str;
if(aa == bb) {
cout << aa << " is a palindromic number." << endl;
exit(0);
} //如果是回文数就终止递归,然后输出
for(int i = 0; i < bb.length(); i++) {
b.push_back(str[i] - '0');
}
//大整数相加
int carry = 0;
int temp = 0;
for(int i = 0; i < max(aa.size(), bb.size()); i++) {
temp = a[i] + b[i] + carry;
c.push(temp % 10);
carry = temp / 10;
}
if(carry != 0) c.push(carry);
int size = c.size(); //防止c.size在弹出栈顶元素的时候减小
char m[2000];
for(int i = 0; i < size; i++) {
sprintf(&m[i], "%d", c.top());
c.pop();
}
string cc = m;
cout << aa << " + " << bb << " = " << cc << endl;
times++;
calculate(cc, times);
}
int main()
{
string str;
int times = 0;
cin >> str;
calculate(str, times);
}
大神的解答:
柳神同样是从头到尾相加,只不过柳神没有使用数组而是直接拿字符串减去‘0’对应的ASCII码进行计算然后进行了倒置,空间复杂度比我小多了。
附柳神的解析:1079. 延迟的回文数 (20)-PAT乙级真题
附柳神的代码:
#include <iostream>
#include <algorithm>
using namespace std;
string rev(string s) {
reverse(s.begin(), s.end());
return s;
}
string add(string s1, string s2) {
string s = s1;
int carry = 0;
for (int i = s1.size() - 1; i >= 0; i--) {
s[i] = (s1[i] - '0' + s2[i] - '0' + carry) % 10 + '0';
carry = (s1[i] - '0' + s2[i] - '0' + carry) / 10;
}
if (carry > 0) s = "1" + s;
return s;
}
int main() {
string s, sum;
int n = 10;
cin >> s;
if (s == rev(s)) {
cout << s << " is a palindromic number.\n";
return 0;
}
while (n--) {
sum = add(s, rev(s));
cout << s << " + " << rev(s) << " = " << sum << endl;
if (sum == rev(sum)) {
cout << sum << " is a palindromic number.\n";
return 0;
}
s = sum;
}
cout << "Not found in 10 iterations.\n";
return 0;
}