2019蓝桥杯每周一题之3n+1问题~好好学习能挣钱~

写在开头:最近开始准备蓝桥杯了,在刷蓝桥杯每周一题,希望自己能在首次参加蓝桥杯能取得自己满意的成绩,加油,好好学习能挣钱。

题目:

[问题描述]

考虑如下的序列生成算法:从整数 n 开始,如果 n 是偶数,把它除以 2;如果 n 是奇数,把它乘 3 加1。用新得到的值重复上述步骤,直到 n = 1 时停止。例如,n = 22 时该算法生成的序列是:22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
人们猜想(没有得到证明)对于任意整数 n,该算法总能终止于 n = 1。这个猜想对于至少 1 000 000内的整数都是正确的。
对于给定的 n,该序列的元素(包括 1)个数被称为 n 的循环节长度。在上述例子中,22 的循环节长度为 16。输入两个数 i 和 j,你的任务是计算 i 到 j(包含 i 和 j)之间的整数中,循环节长度的最大值。

[输入]

输入每行包含两个整数 i 和 j。所有整数大于 0,小于 1 000 000。

[输出]

对于每对整数 i 和 j,按原来的顺序输出 i 和 j,然后输出二者之间的整数中的最大循环节长度。这三个整数应该用单个空格隔开,且在同一行输出。对于读入的每一组数据,在输出中应位于单独的一行。

[样例输入]

1 10
100 200
201 210
900 1000

[样例输出]

1 10 20
100 200 125
201 210 89
900 1000 174

想法:

初次读这道题的时候,有点觉得摸不到头脑,再读仔细一想还是有那么一些想法的,所以得多读几遍题,一是提取出题中所有的有用信息,便于解题,二是有些细节可能不影响某个答案,但是在判题的时候可能导致错误。经过初步的分析后,首先我想到不是怎么解这个题,而是想到了在计算的过程中,很有可能会重复造轮子,当然这取决于输入的数,输入的数越大,两个数之间的范围越大,重复造的轮子就越多,很大程度上会影响时间复杂度,该种解法写在解法2,一般解法即解法1就比较简单了,写一个函数用来算循环节长度,在主函数for循环中反复调用这个函数直到计算完给出的两个数之间的所有数,这其中在用一个容器将每个数的循环节长度,存起来,最后把最大的循环节长度提取出来。解法2就是在解法1的基础上,每计算一个数的时候,先在容器里进行检索,看看是否已经计算过了,如果已经计算了就直接调用该数的循环节长度,没计算就计算后存在容器里,当然检索的过程,也有很多门道了,要想继续优化,可以在检索上继续研究。

解法1:

import java .util.Scanner;
import java.util.Arrays;
public class week1_01 {
    static int  solue(long f) {  //为什么用long而不是int,因为在运算的过程中一个数有可能连续做好几个3n+1,如果这个数比较大的话,就有可能超过int的范围
        int count=1;  //循环节长度
        while (f != 1) {
            if (f % 2 == 0) {
                f = f / 2;
                count++;
            } else {
                f = 3 * f + 1;
                count++;
            }
        }
        return count;
    }
    public static void main(String args[]){
        Scanner sc =new Scanner(System.in);
        int m=sc.nextInt();
        int n=sc.nextInt();
        int temp1,temp2;
        if(m>n){  //有可能不是小数在前,先判断,保证循环的时候小的数在前面
            temp1=m;
            temp2=n;
        }else{
            temp1=n;
            temp2=m;
        }
        int temp3=temp1-temp2+1;//存循环节长度的数组的容量,比如0到10之间有11个数
        int i,j;
        int[]num=new int[temp3];
        for(j=temp2,i=0;j<=temp1;j++,i++){ //假如0到10,i等于10的时候,循环该结束,下一次循环无法进行,但是i还是会加1;
            num[i]=solue(j);
        }
        Arrays.sort(num,0,i); //排序,下标0到i,但并不包括下标为i的那个数
        System.out.println(temp2+" "+temp1+" "+num[i-1]);  //循环节长度最大的数
        // System.out.println(solue(159487)); 检验一下大的数是否能计算出
    }
}

运行结果

在这里插入图片描述

解法2:

最开始我想到的是,用数组作为容器,但是数组做容器有很多不方便的地方,比如我检索的是时候,无论用什么方法检索,我都希望检索到的数通过某种方式对应着该数的循环节长度,如果用数组的话,如果将下标设置为检索的数,而该下标存储的数为循环节长度,那么要检索一个很大很大的数,那么数组的长度将十分大,浪费空间,并且定义数组的时候,初始化长度也不能确认。当我看到官方给出的题解的时候,让我醍醐灌顶,采用hashmap这个容器简直就是,完美匹配

代码如下:


//不愧是官方题解,每一步都很巧妙
import java.util.HashMap;
import java.util.Scanner;

public class week1_02 {
    static HashMap<Integer, Integer> map = new HashMap<>();  //泛型

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int i = sc.nextInt();
            int j = sc.nextInt();
            int a = i;
            int b = j;
            if (i > j) {
                a = j;
                b = i;
            }
            int max = -1;
            for (int k = a; k <= b; k++) {
                Integer fk = map.get(k);//检索步骤,假如k等于900,假如还没有计算过900这个数,hashmap中就不存在这个数,fk=null,假如已经存在,就直接取到了900的循环节长度
                if (fk == null) {  //如果不存在fk这个数,
                    fk = f(k);   //计算该数的循环节长度
                    map.put(k, fk);//存入hashmap中
                }
                max = Integer.max(max, fk);//把前后两次大的循环节长度存为max,遇到更大的不断替换
            }
            System.out.println(i + " " + j + " " + max);
        }
    }

    static int f(long k) {
        int count = 1;
        while (k != 1) {
            if ((k & 1) == 0) {
                k /= 2;
            } else {
                k = k * 3 + 1;
            }
            count++;
        }
        return count;
    }
}

运行结果

在这里插入图片描述

结束语:

路还很长,坚持!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值