UVa 263 Number Chains
2026/5/22 11:17:35 网站建设 项目流程

题目分析

本题要求我们对给定的正整数进行一种特定的数字变换,并记录变换过程中形成的“数字链”,直到出现重复的数字为止。变换规则如下:

  1. 将当前数字的各位数字按降序排列,得到一个大数bigbigbig
  2. 将当前数字的各位数字按升序排列,得到一个小数smallsmallsmall
  3. 计算差值difference=big−smalldifference = big - smalldifference=bigsmall
  4. 用差值作为新的数字,重复上述步骤,直到新生成的数字已经在之前的链中出现过为止。

需要注意:

  • 数字允许包含前导零(例如123412341234的升序排列是123412341234,降序是432143214321;而308730873087的升序是0378=3780378 = 3780378=378,降序是873087308730)。
  • 输入数字小于10910^9109,最多有500050005000个数字。
  • 每条链的长度定义为链中不同数字的个数。
  • 输出格式要求:每个数字链的输出后跟一个空行。

解题思路

1. 问题本质

这个问题的本质是模拟一个确定的数字变换过程,并检测循环。由于每一步变换的结果完全由当前数字的各位数字决定,因此这是一个确定性系统。由于数字范围有限(小于10910^9109,即最多999位),可能的数字状态是有限的,因此必然会进入循环。

2. 算法设计

我们需要对每个输入数字:

  1. 输出原始数字(格式要求第一行)。
  2. 使用一个集合(set)记录已经出现过的数字。
  3. 循环进行变换:
    • 将当前数字转换为字符串,以便对各位数字进行排序。
    • 分别按降序和升序排序得到bigbigbigsmallsmallsmall
    • 计算差值。
    • 输出当前步骤的算式。
    • 检查差值是否已经在集合中:
      • 如果已存在,说明链结束,输出链长度。
      • 否则,将差值加入集合,更新当前数字,链长度加111

3. 细节处理

3.1 数字与字符串的转换

C++中,可以使用to_string将整数转换为字符串,使用stoi将字符串转换为整数。

3.2 排序规则
  • 降序排列:按字符从大到小排序,对应较大的数字。
  • 升序排列:按字符从小到大排序,对应较小的数字。

注意:升序排列后可能产生前导零(例如100100100的升序是001=1001 = 1001=1),但stoi会自动忽略前导零,因此无需特殊处理。

3.3 链的长度统计

题目要求链的长度是链中不同数字的个数。在我们的算法中,当新生成的差值已经在集合中出现时,我们将它再次加入集合,此时链的长度lengthlengthlength已经记录了集合中元素的个数。

3.4 循环终止条件

循环在发现新生成的差值已经在集合中时终止。例如444444444的情况:

  • 原始数字444444444,降序444444444,升序444444444,差值为000
  • 000未出现过,继续。
  • 下一轮:000的降序和升序都是000,差值为000,此时000已经在集合中,终止。
    这样链的长度为222,与示例输出一致。

4. 复杂度分析

  • 每个数字最多变换100010001000次(题目保证)。
  • 每次变换需要排序,数字位数最多999,排序复杂度可视为O(9log⁡9)≈O(1)O(9 \log 9) \approx O(1)O(9log9)O(1)
  • 集合的插入和查找操作平均O(log⁡L)O(\log L)O(logL),其中LLL是当前链长度,L≤1000L \leq 1000L1000
  • 总时间复杂度:对于每个输入数字,O(1000log⁡1000)≈O(104)O(1000 \log 1000) \approx O(10^4)O(1000log1000)O(104),对于500050005000个数字完全可行。

代码实现

// Number Chains// UVa ID: 263// Verdict: Accepted// Submission Date: 2016-05-10// UVa Run Time: 0.380s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;// 降序比较函数,用于将数字的各位从大到小排列boolcmp1(charx,chary){returnx>y;}// 升序比较函数,用于将数字的各位从小到大排列boolcmp2(charx,chary){returnx<y;}intmain(){cin.tie(0);cin.sync_with_stdio(false);intnumber;// 持续读入数字,直到遇到 0 为止while(cin>>number,number){// 输出原始数字,格式要求第一行cout<<"Original number was "<<number<<endl;intlength=1;// 链的长度,初始包含原始数字set<int>appeared;// 记录已经出现过的数字while(true){// 将当前数字转为字符串,以便对各位数字排序string text=to_string(number);// 降序排列得到大数sort(text.begin(),text.end(),cmp1);intbig=stoi(text);// 升序排列得到小数sort(text.begin(),text.end(),cmp2);intsmall=stoi(text);// 计算差值intdifference=big-small;// 输出当前步骤的算式cout<<big<<" - "<<small<<" = "<<difference<<endl;// 如果差值已经在链中出现过,则链结束if(appeared.count(difference)>0){cout<<"Chain length "<<length<<endl;break;}elseappeared.insert(difference);// 将新差值加入集合number=difference;// 用差值继续下一轮变换length++;// 链长度增加}// 每个数字链输出后跟一个空行cout<<endl;}return0;}

总结

本题是一个简单的模拟题,核心在于正确实现数字各位的排序与差值计算,并利用集合检测循环。代码结构清晰,使用set记录历史数字,避免了复杂的循环检测逻辑。注意输出格式中的空行要求,以及链长度的正确计算方式。通过本题可以熟悉C++中字符串与整数的转换、排序函数以及集合的使用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询