7-28 猴子选大王 (20 分)

一、题目要求

一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?

输入格式:

输入在一行中给一个正整数N(≤1000)。

输出格式:

在一行中输出当选猴王的编号。

输入样例:

11

输出样例:

7

二、代码

版本一、用cnt记录往后数的顺序,用数组Quit[i]记录退出与否与顺序。

好久未写,逻辑差点没理清。本题要点如下:
1)数组循环:i = i%N
2)何时序数+1,何时退出,需要准确判断条件并予以记录。
quit[i]==0 --> +1
quit[i]==0 && cnt%3 ==0 --> 退出

import java.util.Scanner;
public class Main {

	public static void main(String[] args) {

		int N, i, cnt=0, monkey=0;
		
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		
		int [] quit = new int[N];
		for(i=0;i<N;i++) {
			quit[i] = 0;
		}
		
		for(i=0;monkey!=N-1;i++, i=i%N ) {
			if(quit[i]==0) {	// 计数。
				cnt++;  // 若该猴子未退出,序数+1
			}
			if(quit[i]==0&&cnt%3==0) {  // 猴子**未退出**且**序数为3**,则退出,并记录是第几个退出的
				monkey++;  // 退出数记录
				quit[i] = monkey;
			}
		}
		
		for(i=0;i<N;i++) {
			if(quit[i]==0) {
				System.out.print(i+1);
			}
		}

	}

}

版本二:公式推导法

这道题本质上是约瑟夫环问题,如何有效推导出来公式是最核心也是最难的地方。

问题:

简单定义下约瑟夫环问题,有n个人,序号1,2,3,…,N-1,N。现在开始从1开始数数,数到M的那个人被杀死。
后面的人从1重新开始数,再数到M的人被杀死。以此往复,直至剩下最后一个人为胜利者。

推导

递推公式: f ( N , M ) = f ( f ( N − 1 ) , M ) + M ) % N   f(N, M) =f(f(N-1),M)+M)\%N\ f(N,M)=f(f(N1),M)+M)%N 

1) f ( N , M ) f(N, M) f(N,M):N个人报数,报M的人退出,最后胜利者编号;
2) f ( N − 1 , M ) f(N-1, M) f(N1,M):N-1个人报数,报M的人退出,最后胜利者编号。

现在用题目N=11,来分析一下退出过程(图来自参考2):
过程
绿色:序号
黄色:退出序号
红色:胜利者编号。

验证

用上图从下—>上来分析一下,7可以看作不同人数时的最终胜利者,他的位置就是胜利者下标:
1)当N=1,7在下标0的位置, f ( 1 , 3 ) f(1, 3) f(1,3)=0;
2)当N=2,7在下标1的位置, f ( 2 , 3 ) f(2, 3) f(2,3)=1;
3)当N=3,7在下标1的位置, f ( 3 , 3 ) f(3, 3) f(3,3)=1;
4)当N=4,7在下标0的位置, f ( 4 , 3 ) f(4, 3) f(4,3)=0;
5)当N=5,7在下标3的位置, f ( 5 , 3 ) f(5, 3) f(5,3)=3;
6)当N=6,7在下标0的位置, f ( 6 , 3 ) f(6, 3) f(6,3)=0;
7)当N=7,7在下标3的位置, f ( 7 , 3 ) f(7, 3) f(7,3)=3;
8)当N=8,7在下标6的位置, f ( 8 , 3 ) f(8, 3) f(8,3)=6;
9)当N=9,7在下标0的位置, f ( 9 , 3 ) f(9, 3) f(9,3)=0;
10)当N=10,7在下标3的位置, f ( 10 , 3 ) f(10, 3) f(10,3)=3;
10)当N=11,7在下标6的位置, f ( 11 , 3 ) f(11, 3) f(11,3)=6;

验证一下:
f ( 1 , 3 ) f(1, 3) f(1,3) = 0;
f ( 2 , 3 ) f(2, 3) f(2,3) =( f ( 1 , 3 ) f(1, 3) f(1,3)+3)%2=1;
f ( 3 , 3 ) f(3, 3) f(3,3) =( f ( 2 , 3 ) f(2, 3) f(2,3)+3)%3=1;
f ( 4 , 3 ) f(4, 3) f(4,3) =( f ( 3 , 3 ) f(3, 3) f(3,3)+3)%4=0;
f ( 5 , 3 ) f(5, 3) f(5,3) =( f ( 4 , 3 ) f(4, 3) f(4,3)+3)%5=3;
f ( 6 , 3 ) f(6, 3) f(6,3) =( f ( 5 , 3 ) f(5, 3) f(5,3)+3)%6=0;
f ( 7 , 3 ) f(7, 3) f(7,3) =( f ( 6 , 3 ) f(6, 3) f(6,3)+3)%7=3;
f ( 8 , 3 ) f(8, 3) f(8,3) =( f ( 7 , 3 ) f(7, 3) f(7,3)+3)%8=6;
f ( 9 , 3 ) f(9, 3) f(9,3) =( f ( 8 , 3 ) f(8, 3) f(8,3)+3)%9=0;
f ( 10 , 3 ) f(10, 3) f(10,3) =( f ( 9 , 3 ) f(9, 3) f(9,3)+3)%10=3;
f ( 11 , 3 ) f(11, 3) f(11,3) =( f ( 10 , 3 ) f(10, 3) f(10,3)+3)%11=6;

可以看到全部正确。

分析

用上图再从上—>下分析一下:
<1>对N个人编号:0,1,2,3,… ,M-2,M-1,M, … ,N-2, N-1;
<2>第一轮退出: 0,1,2,3,…, M-2, M,…,N-2,N-1.
<3>重新排列: M,…,N-1,N,0,1,2,3,…,M-2,(*)
<4>重新编号: 0,1, 2, 3, …, N-3, N-2,(**);

所以(<4>+M)%N == <3> ,即可得只要得出N-1次序列<4>,即可得出第N次退出的位置:(<4>+M)%N

本次的例子可具体分析如下:
推导过程
黄色:退出
绿色:补上下一步退出的

import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
		int quit=0, i=0, N=0;
		
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		
		for(i=2;i<=N;i++) {
			quit = (quit+3)%i;
		}
	    
		System.out.println(quit+1);
	}
}

三、参考

1、通用约瑟夫环解法:PAT-JAVA-5-28 猴子选大王 (20分)

#include<iostream>  
using namespace std;  
int main()  
{  
    int N;//人的总个数  
    int M;//间隔多少个人  
  
    cin>>N;  
    cin>>M;  
    int result=0;//N=1情况  
    for (int i=2; i<=N; i++)  
    {  
        result=(result+M)%i;  
    }  
    cout<<"最后自杀的人是:"<<result+1<<endl;//result要加1  
    return 0;  
}  

2、(五星推荐,清晰简洁)约瑟夫环原理讲解:约瑟夫环——公式法(递推公式)

3、约瑟夫环细节讲解:约瑟夫问题实现的方法总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值