题目:n个玩家围成一个圈,从第一个玩家开始报数,报到m的人被枪毙,下一个人接着从1开始报数报到m的人被枪毙,下一个人接着从1开始报数,直到最后一个活下来的人获得胜利。已知参与的玩家数量为n,被枪毙的数是m,要选到几号位置才会活下来,(1<m<n<100)
设计一个程序,输入n和m,输出最后的胜利者
样例输入:10 3
样例输出:4
暴力解法
直接根据题目要求按部就班的求,不多说了直接上代码
import java.util.Arrays; import java.util.Scanner; public class Text5_03 { public static void main(String[] args) { //创建Scanner对象 Scanner sc = new Scanner(System.in); //提示用户输入 并接收n,m System.out.print("n = "); int n = sc.nextInt(); System.out.print("m = "); int m = sc.nextInt(); // alive表示对应编号参与者是否被枪毙 并赋初值为1 // 1 表示未被枪毙 // 0 表示已被枪毙 int [] alive = new int[n]; Arrays.fill(alive,1); //cor%n用于表示当前循环到编号为几的参与者 利用取模运算作循环数组 int cor = 0; //aliveNum表示剩余人数 当剩余人数为1时停止循环 int aliveNum = n; while(aliveNum > 1){ //向后报数 int key = m; while(key > 0){ //if语句判断是否为活人 活人才能报数 if(alive[cor%n] == 1){ key--; } cor++; } //令其alive值为0表示被枪毙 并将剩余人数减一 //最后一次cor多加了一次 alive[(cor-1)%n] = 0; aliveNum--; } //循环找到胜利者 while(alive[cor%n] != 1){ cor++; } //打印胜利者编号 System.out.println(cor%n + 1); //关闭Scanner对象 sc.close(); } }
动态规划
我们来想一下,假如有11个人,每4个人杀一个,第9个人会活下来
假如有10个人,每4个人杀一个,第5个人会活下来
假如有9个人,每4个人杀一个,第1个人会活下来
好像有某种规律。
不妨这样考虑问题,以下考虑的情形都是每m个人杀一个,且m<n。
已知:现在有n个人,第x个人活下来了(称其为A吧)
求解:如果现在有n+1个人。现在第一轮死掉了一个人,但是这个人不是A。
剩下n个人接着玩这个游戏,最后A活下来了,那么A应该是第几个人呢?
接着玩这个游戏的时候,第一个报1的人,其实是最开始第m+1位置上的人
也就是如果把剩下n个人的序号都往前推m个(前m个人就去队尾了)就和已知是一个问题了
“第一个人” | A | |
---|---|---|
n个人玩的时候的序号 | 1 | x |
n+1个人玩的时候的序号 | m+1 |
因为众人绕成环,是连续的,所以可以推出此时A的序号是x+m
并且由于x+m可能会大于n,所以严谨来讲A的序号应该为 (x+m)%n
有递推公式 f(N,M)=(f(N−1,M)+M)%N
在数组中index从0开始 最后要记得加一。
后话:实际操作时index从0开始更好,假如有5个人,编号是0~4,取模5之后的结果也是0~4
但是如果index从1开始取,这五个人的编号是1~5,取模5之后结果是0~4会出现问题
import java.util.Scanner; public class Text5_03NEW { public static void main(String[] args) { //创建Scanner对象 Scanner sc = new Scanner(System.in); //提示用户进行输入 n,m System.out.print("Enter n = "); int n = sc.nextInt(); System.out.print("Enter m = "); int m = sc.nextInt(); //约瑟夫环问题 属于动态规划问题 //可得出递推公式: f(N,M)=(f(N−1,M)+M)%N int[] alive = new int[n+1]; alive[1] = 0; for (int i = 2; i <= n; i++) { alive[i] = (alive[i - 1] + m) % i; } //打印胜利者编号 System.out.println(alive[n] + 1); //关闭Scanner对象 sc.close(); } }