闲聊蓝桥杯JAVA - 约瑟夫环

闲聊蓝桥杯JAVA - 约瑟夫环

D:现在我们要研究一道不是很熟悉的题目,约瑟夫环

约瑟夫环就是循环不断地减少数量的规则  
问题:41人循环报数,数到3的就自杀,最后两个不想自杀,请问最后两个是谁?

Z:这道题之前的解决过程是这样的:

    public static void main(String[] args) {
        int[] arr=new int[41];
        int point=0;
        int deal=0;
        int num=0;
        while(deal<39){
            if(arr[point]==0){
                num++;
            }
            if(num==3){
                arr[point]=1;
                deal++;
                num=0;   //第三个后念出的数字注意清0
            }
            point=(point+1)%41;   //point递增但不超出循环
        }
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==0){
                System.out.println(i+1);  //序号需要加1
            }
        }
    }

M:这段代码有什么作用?

            if(arr[point]==0){
                num++;
            }

Z:arr是一个存活数组,其长度有41位,每位代表一个人,而它的值0说明存活,1说明已经挂了。这个判断的意思就是让存活者报数。

M:那这一段是什么意思呢?

            if(num==3){
                arr[point]=1;
                deal++;
                num=0;   //第三个后念出的数字注意清0
            }

Z:当报数为3的那个人,它的存活数组值改为1,表示已经挂了。然后deal表示挂了的数量,num重置报数。

M:但是point=(point+1)%41;在这里什么作用呢?

Z:表示当前报数的人的编号,本来只要point++;就可以了,但是因为这是一个环的循环,所以当point变成42的时候,要转化为1。

M:总结一下,这道题的核心就在于创建一个生存的数组,该数组用0,1表示对应值是否生存。根据num增加来确定数字3的落脚点。

D:相类似的题目还有分糖果:

有n个小朋友围坐成一圈。老师给每个小朋友随机发偶数个糖果,然后进行下面的游戏:    
每个小朋友都把自己的糖果分一半给左手边的孩子。  
一轮分糖后,拥有奇数颗糖的孩子由老师补给1个糖果,从而变成偶数。  
反复进行这个游戏,直到所有小朋友的糖果数都相同为止。  
你的任务是预测在已知的初始糖果情形下,老师一共需要补发多少个糖果。  
输入格式  
程序首先读入一个整数N(2<N<100),表示小朋友的人数。  
接着是一行用空格分开的N个偶数(每个偶数不大于1000,不小于2)  
输出格式  
要求程序输出一个整数,表示老师需要补发的糖果数。  

样例输入  
3  
2 2 4  
样例输出  
4  

Z:这道题以前完成过,是这样写的:

    static int addSweet=0;   //初始化补发的糖果数
    public static void main(String[] args) {

        Scanner input=new Scanner(System.in);

        //获取学生数
        int student=input.nextInt();

        //定义每个人的糖果数数组
        int[] sweet=new int[student];
        for (int i = 0; i < sweet.length; i++) {
            sweet[i]=input.nextInt();
        }

        //定义分出的糖果数组
        int[] temp=new int[student];
        //while循环
        while(true){
            //执行糖果变化的方法
            chang(sweet,temp);
            //执行检查是否相同
            if(check(sweet)){
                System.out.println(addSweet);
                return;
            }
            //执行补发糖果的操作,传补发糖果数,返回是否进行补发,判断为没有补发break循环
            add(sweet);   
            //执行检查是否相同
            if(check(sweet)){
                System.out.println(addSweet);
                return;
            }
        }
    }

    public static boolean check(int[] sweet) {
        // TODO Auto-generated method stub
        boolean flag=true;   //初始化判断都相同
        for (int i = 0; i < sweet.length; i++) {
            for (int j = 0; j < sweet.length; j++) {
                if(sweet[i]!=sweet[j]){   //产生了不同
                    flag=false;
                }
            }
        }
        return flag;
    }

    public static void chang(int[] sweet, int[] temp) {
        //数组每个除以2
        for (int i = 0; i < sweet.length; i++) {
            sweet[i]=sweet[i]/2;
            //遍历存进temp数组里
            temp[i]=sweet[i];
        }
        for (int i = 0; i < sweet.length; i++) {
            if(i==0){  //当最后一个的时候跟第一个拿
                sweet[i]=sweet[i]+temp[temp.length-1];
            }else{   //这是选择发生的事件
                sweet[i]=sweet[i]+temp[i-1]; //补充糖果             
            }
        }
    }

    public static void add(int[] sweet) {

        //循环判断,如果是奇数,补充一个糖果,添加计数
        for (int i = 0; i < sweet.length; i++) {
            if(sweet[i]%2!=0){
                sweet[i]++;
                addSweet++;
            }
        }
    }   

M:while(true){是干嘛的?

Z:因为糖果循环的次数是不确定的,所以使用无限循环。

M:那这个chang()方法是干什么?

    public static void chang(int[] sweet, int[] temp) {
        //数组每个除以2
        for (int i = 0; i < sweet.length; i++) {
            sweet[i]=sweet[i]/2;
            //遍历存进temp数组里
            temp[i]=sweet[i];
        }
        for (int i = 0; i < sweet.length; i++) {
            if(i==0){  //当最后一个的时候跟第一个拿
                sweet[i]=sweet[i]+temp[temp.length-1];
            }else{   //这是选择发生的事件
                sweet[i]=sweet[i]+temp[i-1]; //补充糖果             
            }
        }
    }

Z:temp是一个空的数组,前边的操作用temp来存储孩子拿出的一半糖果数。而后边的操作是,将这一半的糖果给旁边的人。其中第一个需要特殊处理,因为他的糖果来自于最后一个。

M:check()方法是检查各糖果数是否相同,add()方法是为奇数的孩子补糖果。在chang()方法后面添加check()检查,但是为什么在add()后面也要添加check()检查?

Z:这是隐藏的题意所在,只要改变糖果数量,就要进行检验check()

M:所以本题的重点就是对糖果转移的处理,通过两个数组的方式来对糖果进行变化。其中要注意转移糖果 & 补糖果 操作之后都要对其进行检验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值