临近数求解

[问题描述]

    问题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
请按任意键继续. . .

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值