C语言之5位黑洞数

题目描述

任意一个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;
} 
相关推荐
CodeByV2 小时前
【算法题】BFS:FloodFill
算法
long3162 小时前
弗洛伊德·沃肖算法 Floyd Warshall Algorithm
java·后端·算法·spring·springboot·图论
有一个好名字2 小时前
力扣-咒语和药水的成功对数
java·算法·leetcode
Loo国昌2 小时前
【LangChain1.0】第一篇:基础认知
后端·python·算法·语言模型·prompt
H Corey2 小时前
Java--面向对象之继承与多态
java·开发语言·windows·学习·算法·intellij-idea
じ☆冷颜〃2 小时前
交换代数的解析延拓及在CS的应用
c语言·数据结构·笔记·线性代数·密码学
永远都不秃头的程序员(互关)2 小时前
【K-Means深度探索(三)】告别“初始陷阱”:K-Means++优化质心初始化全解析!
算法·机器学习·kmeans
程序员-King.2 小时前
day136—快慢指针—重排链表(LeetCode-143)
算法·leetcode·链表·快慢指针
万行2 小时前
差速两轮机器人位移与航向角增量计算
人工智能·python·算法·机器人