[问题描述]
问题1. 计算正数num1的上临近数num2,num2满足:
(1) num2 > num1;
(2) num1、num2各位数字之和相等(十进制表示);
(3) num2是满足(1)、(2)条件的最小正数
问题2. (另一个相似问题) 计算正数num1的下临近数num2,num2满足(注意一个数的下临近数可能不存在):
(1) num2 < num1;
(2) num1、num2各位数字之和相等(十进制表示);
(3) num2是满足(1)、(2)条件的最大正数
[问题分析与解答]
首先,问题1和问题2 都可以通过穷举解答,但当数非常大时其效率是很低的。从另一方面讲,如果穷举方法有效的话,也没有本文了:-)
不用穷举方法,我们只能从数字本身的特性进行分析,其实你会很快发现其方法非常简单。这里直接给出直观解答(有兴趣自己证明),并附上C++实现:
解答1:
把num表示为x[k]...x[1]a9...9b0...0,其中a != 9, b != 0(前面的x[i]可能没有)
step1:将原串变为x[k]...[1]ab0...09...9
step2:再次变换为x[k]...x[1](a+1)0...0(b-1)9...9,over
解答2:
把num表示为x[k]...x[1]a0...0b9...9,其中a != 0, b != 9(当下临近数不存在时,无法表示成这种形式)
step1:x[k]...x[1]a9...90...0b
step2:x[k]...x[1](a-1)9...9(b+1)0...0
C++实现:
#include <iostream> #include <string> #include <sstream> using namespace std;
/** * 计算正数num1的上临近数num2,num2满足: * (1) num2 > num1; * (2) num1、num2各位数字之和相等(十进制表示); * (3) num2是满足(1)、(2)条件的最小正数 * @param num: num1的值,应为正数 * @return: 如果@param num为正数,则返回num1的上临近数num2;否则返回-1 */ int get_up_nearest(size_t num) { if (num == 0) return -1; // 0没有上临近数
ostringstream oss; oss << num; string src("0"); // 保证下面的a的存在,并且不会影响结果 src += oss.str();
// 把num表示为x[k]...x[1]a9...9b0...0,其中a != 9, b != 0(前面的x[i]可能没有) // step1:将原串变为x[k]...[1]ab0...09...9 // step2:再次变换为x[k]...x[1](a+1)0...0(b-1)9...9,over typedef string::size_type pos_t;
pos_t b = src.find_last_not_of('0'); pos_t a = src.find_last_not_of('9', b - 1);
// 计算x[k]...[1]ab0...09...9中"最后一个"0的位置,即x[k]...[1]ab0...c9...9 pos_t c = src.length() - b + a; ++src[a]; src[c] = src[b] - 1; for (pos_t i = a + 1; i < c; ++i) src[i] = '0'; for (pos_t i = c + 1; i < src.length(); ++i) src[i] = '9'; int res = atoi(src.c_str());
return res; }
/** * 计算正数num1的下临近数num2,num2满足: * (1) num2 < num1; * (2) num1、num2各位数字之和相等(十进制表示); * (3) num2是满足(1)、(2)条件的最大正数 * @param num: num1的值;对于某些特殊的num值,可能不存在相应的下临近数 * @return: 如果存在下临近数,则返回下临近数;否则返回-1 * @note: 并不是所有数都存在下临近数,例如0~9,还如x99...9这类数就不存在下临近数 */ int get_down_nearest(size_t num) { if (num < 10) return -1; // 不存在下临近数 ostringstream oss; oss << num; string src = oss.str(); // 把num表示为x[k]...x[1]a0...0b9...9,其中a != 0, b != 9 // step1:x[k]...x[1]a9...90...0b // step2:x[k]...x[1](a-1)9...9(b+1)0...0 typedef string::size_type pos_t;
pos_t b = src.find_last_not_of('9'); if (b == string::npos || b == 0) return -1; // num形如:x99...9,此时不存在下临近数 pos_t a = src.find_last_not_of('0', b - 1); // 计算x[k]...x[1]a9...90...0b中"第一个"0的位置,即x[k]...x[1]a9...9c...0b pos_t c = src.length() - b + a; --src[a]; src[c] = src[b] + 1; for (pos_t i = a + 1; i < c; ++i) src[i] = '9'; for (pos_t i = c + 1; i < src.length(); ++i) src[i] = '0'; int res = atoi(src.c_str()); return res; }
void test(int num) { cout << "num = " << num << ", up-nearest = " << get_up_nearest(num) << ", down-nearest = " << get_down_nearest(num) << endl; }
int main() { test(113); test(10); test(200); test(1); test(2); test(9); test(99); test(1999); test(4684); test(4660500); return 0; }
运行结果如下: num = 113, up-nearest = 122, down-nearest = 104 num = 10, up-nearest = 100, down-nearest = 1 num = 200, up-nearest = 1001, down-nearest = 110 num = 1, up-nearest = 10, down-nearest = -1 num = 2, up-nearest = 11, down-nearest = -1 num = 9, up-nearest = 18, down-nearest = -1 num = 99, up-nearest = 189, down-nearest = -1 num = 1999, up-nearest = 2899, down-nearest = -1 num = 4684, up-nearest = 4693, down-nearest = 4675 num = 4660500, up-nearest = 4661004, down-nearest = 4660410 请按任意键继续. . .