输入包含多组数据。
每组数据包含两个正整数 n 和m(1≤ m < n ≤ 10000)。
对应每一组输入,输出猴王的编号 i(1≤i≤n)。
7 3<br/>8 3
4<br/>7
据说著名***历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个***人与Josephus及他的朋友躲到一个洞中,39个***人决定宁愿死也不要被敌人抓到,于是决定了一个***方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须***,然后再由下一个重新报数,直到所有人都***身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了人数总和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
构建一个列表,按照报数顺序依次 删除元素,直到只剩下一个元素
/**
* 常规算法
* 序列为 1,2,... ,n
*/
static int Josephus(int n,int m){
List<Integer> monkeys=new ArrayList<>();
for(int i=1;i<=n;i++){
monkeys.add(i);
}
int pos=0;
while(n>1){
pos+=m-1;
pos%=n;
monkeys.remove(pos);
n--;
}
return monkeys.get(0);
} 假设 对于n,m 已知 k 是当前需要***的人,当前序列为
0,1,... ,k-1, k , k+1, ... , n-1
那么剩下的人分别为
0,1,... ,k-1, k+1, ... , n-1
接下来需要从 k+1 处开始报数,不妨对 k 以前的数 +n, 即:
n, n+1, ... , n+k-1, k+1, ... , n-1
按照报数顺序排列:
k+1, ... , n,n+1, ... , n+k-1
这个序列可看出,仅比正常序列 0,1,... , n-2 多了 k+1
所以有:
假设 josephus(n,m)是序列 0,1,... , n-1循环报数最终剩下的人,
则 josephus(n-1,m)为序列 0,1,... , n-2循环报数最终剩下的人
josephus(n,m)= josephus(n-1,m)+k+1
考虑到运用上述公式时,是对 n,m序列 对k之前的数+n得到的,
因此应当修正为
josephus(n,m)=( josephus(n-1,m)+k+1)%n
如此即可还原到原始序列。
又因为对于任意 n,m ,初始k值我们是可以求解得到的
k=(m%n)-1
所以
josephus(n,m)=( josephus(n-1,m)+(m%n))%n josephus(n,m)=( josephus(n-1,m)+m)%n
至此已形成递归
递归截至条件也显而易见
josephus(1,m)=0
注意
此解法针对的是 0, 1, 2, ... , n-1的序列,
但约瑟夫环一般是从1 开始的,即 1, 2, ... , n
所以我们利用 josephus(n,m)求出的结果要 +1才是最终结果。
public class Main {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
while (scanner.hasNext()){
//递归算法
System.out.println( JosephusRecursive( scanner.nextInt(), scanner.nextInt() )+1 );
//常规算法
// System.out.println( Josephus( scanner.nextInt(), scanner.nextInt() ));
}
}
/**
* 递归算法
* 序列为 0,1,2,... ,n-1
*/
static int JosephusRecursive(int n,int m){
if(n==1){
return 0;
}
return (JosephusRecursive(n-1,m)+m)%n;
}
} 由 递归法 可知
josephus(n,m)=( josephus(n-1,m)+m)%n josephus(1,m)=0
因此有
josephus(n+1,m)=( josephus(n,m)+m)%(n+1)
将 josephus( ) 简记为 J( )
J ( n+1, m ) = ( J ( n, m ) + m ) % ( n+1 )
由此可以将递归改成循环
/**
* 递归衍生循环
*/
static int JosephusCycle(int n,int m){
int r=0;
for(int i=2;i<=n;i++){
r = (r+m)%i;
}
//由于该方法为递归思想推算过来的,上述算法执行完后的结果仍然为 针对序列 0, 1, ... , n-1推算的结果,
// 因此最终结果需要 +1
return r+1;
} 该方法虽然是从递推的结果进一步通过数***算得来的,但是结论反而可以当作一个公式用,只是逻辑上讲没什么道理。
#include<stdio.h>
int main(){//the shorter,the better.
int n,m,r,i;
for(;~scanf("%d%d",&n,&m);printf("%d\n",r+1))for(r=0,i=2;i<=n;r=(r+m)%i,i++);
}
递推公式求解法:
#include <iostream>
using namespace std;
int main() {
int m, n;
while (scanf("%d %d", &n, &m) != -1) {
//对于f(1, m),称王的猴子下标为0
int index = 0;
//递推公式f(n, m) = (f(n - 1, m) + m) % n,求出f(2, m),f(3, m)、...、f(n, m)
for (int i = 2; i <= n; ++i) {
index = (index + m) % i;
}
//我们求出的是下标,需要+1得到序号
printf("%d\n", index + 1);
}
return 0;
}
————————————————
版权声明:本文为CSDN博主「hestyle」的原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://hestyle.blog.csdn.net/article/details/104739822 报数过程模拟实现(超时)
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int m, n, start, count;
//table[i]记录编号为i + 1的猴子是否还在圈中
bool table[10001] = {0};
while (scanf("%d %d", &n, &m) != -1) {
start = 0;//当前开始报数的人下标
count = n;//圈中剩余的猴子
//初始化所有人在圈中
memset(table, 1, n);
//模拟报数,直到圈中剩余1个猴子(注意最后退出的时候count == 1,但执行--操作的时候count == 0)
while (count-- > 1) {
//模拟一次报数(淘汰一个猴子)
for (int i = 1; i <= m; ++i) {
//找到下一个在圈中的猴子
while (table[start] == false) {
start = (start + 1) % n;
}
if (i == m) {
//报到第m个数的猴子出圈
table[start] = false;
}
//计数器后移
start = (start + 1) % n;
}
}
//然后找出唯一剩下的猴子的下标
while (count < n) {
if (table[count++]) {
printf("%d\n", count);
break;
}
}
}
return 0;
}
————————————————
版权声明:本文为CSDN博主「hestyle」的原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://hestyle.blog.csdn.net/article/details/104739822
//开向量直接模拟
#include <cstdio>
(802)#include <vector>
using std::vector;
vector<int> A;
int main(int argc, char const *argv[]){
int n, m;
while(scanf("%d%d", &n, &m) != EOF){
if(m == 1){
printf("%d\n", n);
}else{
for (int i = 1; i <= n; ++i) {
A.push_back(i);
}
int pos = m - 1;
while(A.size() > 1){
while(pos <= ((int) A.size() - 1)){
A.erase(A.begin()+ pos);
pos += (m -1);
}
int TSize = (int) A.size();
pos = pos % TSize;
}
printf("%d\n",A[0]);
A.clear();
}
}
return 0;
}