题目描述
任意一个5位数,比如:34256,把它的各位数字打乱,重新排列,可以得到一个最大的数:65432,一个最小的数23456。求这两个数字的差,得:41976,把这个数字再次重复上述过程(如果不足5位,则前边补0)。如此往复,数字会落入某个循环圈(称为数字黑洞)。
比如,刚才的数字会落入:[82962, 75933, 63954, 61974] 这个循环圈。
输出
请编写程序,找到5位数所有可能的循环圈,并输出,每个循环圈占1行。其中5位数全都相同则循环圈为 [0],这个可以不考虑。
其中数字的先后顺序可以不考虑。
cs
输出
[82962, 75933, 63954, 61974]
cs
#include <stdio.h>
#include <stdlib.h>
// 计算下一个数(最大数-最小数)
int next_number(int n) {
int digits[5];
int i, j, temp;
// 提取各位数字(不足5位时高位补0)到digits数组中(将数字转换成数组)
for (i = 4; i >= 0; i--) {
digits[i] = n % 10;
n /= 10;
}
// 获取最小数(升序排列)
int min_digits[5];//因为既要升序排,也要降序排,所以重新定义两个数组来记录
for (i = 0; i < 5; i++) {
min_digits[i] = digits[i];
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4 - i; j++) {
if (min_digits[j] > min_digits[j + 1]) {
temp = min_digits[j];
min_digits[j] = min_digits[j + 1];
min_digits[j + 1] = temp;
}
}
}
// 获取最大数(降序排列)
int max_digits[5];
for (i = 0; i < 5; i++) {
max_digits[i] = digits[i];
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4 - i; j++) {
if (max_digits[j] < max_digits[j + 1]) {
temp = max_digits[j];
max_digits[j] = max_digits[j + 1];
max_digits[j + 1] = temp;
}
}
}
// 计算最小数和最大数
int min_num = 0, max_num = 0;
for (i = 0; i < 5; i++) {//将数组转换成数字
min_num = min_num * 10 + min_digits[i];
max_num = max_num * 10 + max_digits[i];
}
return max_num - min_num;
}
// 检查两个循环圈是否相同(顺序无关)
int same_cycle(int cycle1[], int len1, int cycle2[], int len2) {
if (len1 != len2) return 0;
// 根据实际观察,5位黑洞数循环圈长度通常不超过6
int temp1[6], temp2[6];
// 数据复制
for (int i = 0; i < len1; i++) {
temp1[i] = cycle1[i];
temp2[i] = cycle2[i];
}
// 排序(使用简单的冒泡排序)
for (int i = 0; i < len1 - 1; i++) {
for (int j = 0; j < len1 - i - 1; j++) {
if (temp1[j] > temp1[j + 1]) {
int t = temp1[j]; temp1[j] = temp1[j + 1]; temp1[j + 1] = t;
}
if (temp2[j] > temp2[j + 1]) {
int t = temp2[j]; temp2[j] = temp2[j + 1]; temp2[j + 1] = t;
}
}
}
// 比较
for (int i = 0; i < len1; i++) {
if (temp1[i] != temp2[i]) return 0;//两个循环圈不同
}
return 1;//两个循环圈相同
}
int main() {
int all_cycles[100][10]; // 存储所有循环圈
int cycle_lengths[100]; // 每个循环圈的长度
int cycle_count = 0; // 找到的循环圈数量
// 遍历所有5位数(10000-99999)
for (int start = 10000; start <= 99999; start++) {
// 跳过各位数字相同的数
int d1 = start / 10000;//提取第一位数字
int d2 = (start / 1000) % 10;提取第二位数字
int d3 = (start / 100) % 10;提取第三位数字
int d4 = (start / 10) % 10;提取第四位数字
int d5 = start % 10;提取第五位数字
if (d1 == d2 && d2 == d3 && d3 == d4 && d4 == d5) {
continue; // 跳过各位数字相同的数
}
int visited[100000] = {0}; // 记录访问过的数字
int sequence[100]; // 记录序列
int seq_len = 0;//访问的数字个数
int current = start;//遍历的五位数的第一个数字
// 寻找循环圈
while (!visited[current]) {//如果遇到已访问的数字,说明找到循环
visited[current] = seq_len + 1; // 从1开始记录每个数字首次出现的位置,等于几表示访问的第几个数字;
//因为五位数字第一次出现时,visited[current]=0,所以总是会满足循环条件,会执行下面的程序。
//但如果是第二次出现,已经执行 visited[current] = seq_len + 1;,就不再等于0了,所以不满足上述循环条件,就退出循环,说明循环圈已经形成。
sequence[seq_len++] = current;//用sequence数组来表示循环圈中的数字,每次循环都会在该数组中加一个数字,最后就是循环圈。
current = next_number(current);//循环圈中该数字的下一个数字,继续上述循环
}
// 提取循环圈
int start_pos = visited[current] - 1;//转换成数组索引,下标从0开始
int cycle_len = seq_len - start_pos;//循环长度
int cycle[10];//用于存储提取出来的循环圈。因为前面的循环圈数组下标是从1开始的,所以应当提取出数字来,从下标0开始
for (int i = 0; i < cycle_len; i++) {
cycle[i] = sequence[start_pos + i];
}
// 检查是否已经存在相同的循环圈(因为循环遍历了所有的五位数,所以难免会遇到有相同循环圈的情况)
int found = 0;//尚未找到相同的循环圈
for (int i = 0; i < cycle_count; i++) {
if (same_cycle(cycle, cycle_len, all_cycles[i], cycle_lengths[i])) {
found = 1;//找到相同循环圈
break;
}
}
// 如果没找到相同的循环圈,添加到列表中
if (!found && cycle_len > 0) {
for (int i = 0; i < cycle_len; i++) {
all_cycles[cycle_count][i] = cycle[i];
}
cycle_lengths[cycle_count] = cycle_len;
cycle_count++;
}
}
// 输出所有循环圈
for (int i = 0; i < cycle_count; i++) {
printf("[");
for (int j = 0; j < cycle_lengths[i]; j++) {
printf("%d", all_cycles[i][j]);
if (j < cycle_lengths[i] - 1) {
printf(", ");
}
}
printf("]\n");
}
return 0;
}
same_cycle函数详细解析:
1.主要目标
判断两个循环圈是否相同,顺序无关。这意味着:
· [1, 2, 3] 和 [3, 1, 2] 应该被认为是相同的
· [1, 2, 3] 和 [1, 2, 4] 是不同的
· [1, 2, 3] 和 [1, 2, 3, 3] 是不同的(长度不同)
2.为什么需要这个函数?
在黑洞问题中:同一个循环圈可能从不同的起点开始;循环圈可能有不同的表示顺序;需要避免重复输出相同的循环圈。
3.设计思路:
比较长度:长度不同直接返回不同;
复制数据:避免修改原始数据;
排序:使顺序无关的比较变为可能
比较:逐元素比较排序后的数据
示例:用数字34256来演示
第一次循环:
visited[34256] = 1
sequence = [34256]
seq_len = 1
current = 41976
第二次循环:
visited[34256] = 1
visited[41976] = 2
sequence = [34256, 41976]
seq_len = 2
current = 82962
第三次循环:
visited[34256] = 1
visited[41976] = 2
visited[82962] = 3
sequence = [34256, 41976, 82962]
seq_len = 3
current = 75933
第四次循环:
visited[34256] = 1
visited[41976] = 2
visited[82962] = 3
visited[75933] = 4
sequence = [34256, 41976, 82962, 75933]
seq_len = 4
current = 63954
第五次循环:
visited[34256] = 1
visited[41976] = 2
visited[82962] = 3
visited[75933] = 4
visited[63954] = 5
sequence = [34256, 41976, 82962, 75933, 63954]
seq_len = 5
current = 61974
第六次循环:
visited[34256] = 1
visited[41976] = 2
visited[82962] = 3
visited[75933] = 4
visited[63954] = 5
visited[61974] = 6
sequence = [34256, 41976, 82962, 75933, 63954, 61974]
seq_len = 6
current = 82962
第七次循环:
visited[82962]=3,循环条件!visited[82962]=0;跳出循环,形成循环圈。
二.解析下面的代码:
// 检查是否已经存在相同的循环圈
int found = 0;
for (int i = 0; i < cycle_count; i++) {
if (same_cycle(cycle, cycle_len, all_cycles[i]//这才是整个数组, cycle_lengths[i])) {
found = 1;
break;
}
}
// 如果没找到相同的循环圈,添加到列表中
if (!found && cycle_len > 0) {
for (int i = 0; i < cycle_len; i++) {
all_cycles[cycle_count][i] = cycle[i];//是一个数组元素
}
cycle_lengths[cycle_count] = cycle_len;
cycle_count++;
}
先找是否有相同的循环圈,然后再添加到列表中,是因为cycle表示的是当前发现的循环圈数组,而all_cycles[i]表示的是已存储的第i个循环圈,二者比较,看看是否重复,决定当前的循环圈是否列入all_cycles[i]。
一开始的时候,还没有填充all_cycles数组内容,所以不会先执行上述的for循环,而是执行下面的if条件,都满足条件,把int all_cycles[0][1];数组的第一行填充完毕,填的是第一个有循环圈的五位数的循环圈,没有比较,所以一定没有重复,所以直接添加到列表中。
然后第二个有循环圈的五位数,调用上述函数开始与已存储的数组进行比较,看看是否重复。
三.输出当然是以二维数组的形式输出,刚好符合题意。
我当时的想法:
没有考虑如果遇到相同的循环圈怎么办?所以有很多代码都没有写出来,这肯定就不对。
也没有遍历,就以题干中举的例子写的特定代码,这就导致我没考虑到循环圈相同的问题;同时没有考虑到输出所有循环圈时的二维数组问题。
下面代码指定没对,输出也没注意(因为我已经知道写的不对了)!
cs
#include<stdio.h>
int bubble_sort(int result)
{
//将该result数字转换成数组的形式
int i;
int arry[5];
for(i=4;i>=0;i--){
int digit=result%10;
arry[i]=digit;
result=result/10;
}
int input1[5];
int input2[5];
for(i=0;i<5;i++){
input1[i]=arry[i];
input2[i]=arry[i];
}
int j;
//最小值
int temp;
for(i=0;i<4;i++){
for(j=0;j<4-i;j++){
if(input1[j]>input1[j+1]){
temp=input1[j];
input1[j]=input1[j+1];
input1[j+1]=temp;
}
}
}
//最大值
for(i=0;i<4;i++){
for(j=0;j<4-i;j++){
if(input2[j]<input2[j+1]){
int temp=input2[j];
input2[j]=input2[j+1];
input2[j+1]=temp;
}
}
}
int min_num=0;
int max_num=0;
for(i=0;i<5;i++){
min_num=min_num*10+input1[i];
max_num=max_num*10+input2[i];
}
return max_num-min_num;
}
int main()
{
int result[100];
int count=0;
int i,j;
result[0]=34256;//第一个完整的数字表示
for(i=1;i<5;i++){
result[i]=bubble_sort(result[i-1]);//result【】表示的是数字,不是单个数字字符
count++;
if(result[i]==result[i-1]){
break;
}
}
if(i>=5){
printf("[0]");
}else{
for(i=1;i<=count;i++){
printf("%d,",result[i]);
}
}
return 0;
}