1005继续(3n+1)猜想

一.问题:

卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。

当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n=3 进行验证的时候,我们需要计算 3、5、8、4、2、1,则当我们对 n=5、8、4、2 进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这 4 个数已经在验证3的时候遇到过了,我们称 5、8、4、2 是被 3“覆盖”的数。我们称一个数列中的某个数 n 为“关键数”,如果 n 不能被数列中的其他数字所覆盖。

现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。

输入格式:

每个测试输入包含 1 个测试用例,第 1 行给出一个正整数 K (<100),第 2 行给出 K 个互不相同的待验证的正整数 n (1<n≤100)的值,数字间用空格隔开。

输出格式:

每个测试用例的输出占一行,按从大到小的顺序输出关键数字。数字间用 1 个空格隔开,但一行中最后一个数字后没有空格。

输入样例:

6
3 5 6 7 8 11

输出样例:

7 6

二.解题思路:

        创建数组nums存储输入的待验证数字,从nums第一个数开始遍历每一个待验证数字,每个待验证数字进行卡拉兹运算,每次进行卡拉兹运算得到的结果与nums所有待验证的字进行比较,如果发现有值相同的待验证数,则说明该待验证数不是关键数,将其赋值为1,将所有数检测完毕之后,数组nums中不为1的数即为关键数,将这些数字取出放入新数组key_num,然后排序输出。

三.个人答案(C语言实现):

#include <stdio.h>
// 用于检验检查数组nums里面是否有和n一样的数字nums[i],有则将nums[i]的值改为1
void judge_keyNum(int n, int *nums, int K)
{
    for (int j = 0; j < K; j++)
    {
        if (nums[j] == n)
        {
            nums[j] = 1;
        }
    }
}

// 用于将经过judge_keyNum处理后的数组nums中的非1元素取出添加到key_num里面,同时函数返回关键数个数
int add_keyNum(int *nums, int *key_num, int K)
{
    int i = 0;
    for (int j = 0; j < K; j++)
    {
        if (nums[j] != 1)
        {
            key_num[i] = nums[j];
            i++;
        }
    }
    return i;
}

// 用于将key_num里面的关键数按从大到小顺序重新排列
void sort(int *key_num, int len)
{
    for (int i = 0; i < len - 1; i++)
    {
        for (int j = 0; j < len - 1 - i; j++)
        {
            if (key_num[j] < key_num[j + 1])
            {
                int t;
                t = key_num[j + 1];
                key_num[j + 1] = key_num[j];
                key_num[j] = t;
            }
        }
    }
}

int main()

{
    int K;            // 用于存储用户输入的待验证的数字数量
    int nums[101];    // 用于存储用户输入的一系列待验证的数字
    int key_num[101]; // 用于存储处理得到的关键数

    // 输入数据,K以及K个待验证的数字,输入时,待验证的数字之间用空格分隔
    scanf("%d", &K);
    for (int i = 0; i < K; i++)
    {
        scanf("%d", &nums[i]);
    }

    // 遍历数组key_num里面的所有待验证的数字
    int i = 0;
    // 由于待验证的正整数 n 的值满足1< n ≤100,因此便利条件可设为nums[i]不为0
    for (; nums[i] != 0; i++)
    {
        // 进行卡拉兹猜想验证
        int n = nums[i];
        while (n != 1)
        {
            if (n % 2 == 0) // n为偶数,n = n/2
            {
                n /= 2;
            }
            else
            {
                n = (3 * n + 1) / 2; // n为奇数,n = (3*n+1)/2
            }

            // 检查数组nums里面是否有和n一样的数字nums[i],有则说明nums[i]一定不是关键数,nums[i]变为1,没有nums[i]不一定是关键数,继续操作
            judge_keyNum(n, nums, K);
        }
    }

    // 将经过judge_keyNum处理后的数组nums中的非1元素取出添加到key_num里面,同时函数返回关键数个数
    int len = add_keyNum(nums, key_num, K);
    // 将key_num里面的关键数按从大到小顺序重新排列
    sort(key_num, len);
    // 输出关键数,由于add_keyNum()函数将关键数添加到数组key_num里面,此时key_num里面的非0元素就是关键数
    for (int i = 0; i < len; i++)
    {
        if (i != len - 1)
        {
            printf("%d ", key_num[i]);
        }
        else
        {
            printf("%d\n", key_num[i]);
        }
    }
    return 0;
}

四.2023.11.12新的解题C代码版本:

        提刷多了后,反过来看当初做的,发现有点麻烦,可以定义一个哈希表标记数字,简单好多,同时排序用快速排序的算法,速度提升好多

#include <stdio.h>

// 定义一个快速排序的函数用于对结果进行排序。
void quick_sort(int arr[],int left,int right)
{
    int i = left;
    int j = right;
    int temp = arr[left];
    int t = 0;
    if(i > j)
    {
        return;
    }
    while(i != j)
    {
        // j端 向前搜索,找到第一个比temp小的数
        while(arr[j] <= temp && i < j)
        {
            j--;
        }
        // i端 向后搜索,找到第一个比temp大的数
        while(arr[i] >= temp && i < j)
        {
            i++;
        }
        // 如果 i < j,交换 arr[i] 和 arr[j] 的值
        if(i < j)
        {
            t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }

        // 将 temp 放到根据 arr[i], arr[j] 调整完位置后的准确位置
        arr[left] = arr[i];
        arr[i] = temp;

        // 对 temp 左右两边的序列继续进行上述排序
        quick_sort(arr,left,i-1);
        quick_sort(arr,i+1,right);
    }
}

int main()
{
    //输入数据 
    int K = 0; // K 存储给定的正整数个数
    int n = 0; // n 用于存放每次输入的待验证的数字
    scanf("%d",&K);
    int num[101] = {0}; // num[] 存放输入的待验证的数字
    int array[105] = {0}; // array[] 用于标记对应的数被覆盖
    for(int i = 0;i < K;i++)
    {
        scanf("%d",&num[i]); // 接收待验证的数字
        n = num[i];
        // 对输入的 n 进行卡拉兹运算
        while(n != 1)
        {
            if(n % 2 == 0) // 当 n 为偶数时
            {
                n /= 2;
                // 如果数的范围小于等于100且大于1,则标记为已覆盖
                if(n <= 100 && n > 1) 
                {
                    array[n]++;
                }
            }
            else // 当 n 为奇数时
            {
                n = (3 * n + 1) / 2;
                // 如果数的范围小于等于100且大于1,则标记为已覆盖
                if(n <= 100 && n > 1) 
                {
                    array[n]++;
                }              
            }
        }
    }

    int result[101] = {0}; // 用于存储关键数
    int count = 0; // 用于记录关键数的数量
    for(int j = 0;j < K;j++)
    {
        // 如果待验证的数字在 array 数组中未被标记(未被覆盖),且值小于等于100,则将其存入 result 中
        if(array[num[j]] == 0 && num[j] <= 100)
        {
            result[count] = num[j];
            count++;
        }
    }

    // 对关键数进行排序
    quick_sort(result,0,count-1);

    // 输出关键数
    // 最后一个数后面没有空格
    for(int k = 0;k < count;k++)
    {
        if(k == count - 1)
        {
            printf("%d\n",result[k]);
        }
        else
        {
            printf("%d ",result[k]);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuga...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值