Josephus问题(约瑟夫环)

这里讲其中一种:

问题描述:

    一堆猴子都有编号,编号是1,2,3 …n ,这群猴子(n个)按照1-m的顺序围坐一圈,从第1开始数,每数到第m个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子为大王。

输入:

    n,m (m, n<10000)

输出:

    大王的编号
模拟实现的代码:

#include <iostream>
#include <cstdio>

using namespace std;
const int N=1e5+100;//猴子个数的范围,可调节
int a[N];//a数组用来存储猴子之间的关系,这题只需要用到后继,然后将尾部与首部相连

void build(int st,int ed)//a[i]的值表示在位置i的猴子的后一个猴子的位置是a[i]
{
    a[ed]=st;//将最后一个猴子指向第一个猴子的位置
    for(int i=st;i<ed;i++)
        a[i]=i+1;//除最后一个猴子,其他猴子都指向下一个猴子
}

int solve_monkey(int m,int n)//对于每组m,n,输出猴子大王的编号
{
    if(n==1) return m;//当n=1时, 最后的猴子一定是最后一个猴子m
    build(1, m);//建立m个猴子的直接后继
    int pos=1;//表示最先从编号为1的猴子开始数,pos表示当前数到的猴子的序号
    int cnt=1;//表示当前数到第几个猴子,因为第一个猴子已数过,故cnt=1
    while(a[pos]!=pos)//a[pos]!=pos 说明下一个猴子不是自己本身,即猴子数>=2个,需继续数猴子
    {
        cnt++;//数的猴子加1,即将序号为a[pos]的猴子已经数过
        if(cnt==n) //当cnt=n时,说明该猴子要出圈
        {
            a[pos]=a[a[pos]];//将pos位的猴子的直接后继(a[pos]) 改为 a[pos]位猴子的直接后继(a[a[pos]])
            pos=a[pos];//继续向后数猴子,当前数到的猴子是序号为更新后的a[pos]的猴子,cnt计数为1
            cnt=1;
        }
        else
            pos=a[pos];//如何不是第n个猴子,继续往后数,将pos向后移动
    }
	//跳出while循环,说明此时猴子个数为1,该猴子的序号为pos,return pos
    return pos;
}



int main()
{
    int c,d;
    while(scanf("%d%d",&c, &d)==2)//c对应题目中的m,d对应题目中的n
    {
		if(c<=0||d<=0) 
		{
			printf("输入数据不合法\n");
			continue;
		}

        printf("%d个猴子,数到%d 个,输出为大王的猴子是:%d号\n", c, d, solve_monkey(c,d));
    }
    return 0;
}

利用数学方法的代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long 


int main()
{
	
	int m,n;
	while(~scanf("%d%d", &n, &m))
	{
		int r=0;
		for(int k=1;k<=n;++k) r=(r+m)%k;
		cout<< r+1 << endl;
	}
	
	
	return 0;
}

理解:(主要是倒推)

    将总数为n依次编号为0,1,2,···,n-1,此轮去掉的编号是(m-1)mod n,现在规定下一轮最先开始数的设为编号0,故此轮编号为(m+i)mod n对应下一轮编号为i\ 。以n=5,m=3为例:

编号
当前轮01234
对应下一轮编号23已去01

反过来,这轮(总数为n-1)的编号为i的对应上一轮(总数为n)编号为 ( m + i ) m o d   n (m+i) mod \ n (m+i)mod n的。比如样例中的下一轮编号为3的,对应的上一轮的编号为(m+3)%n=(3+3)%5=1.
由此,这样就可以当总数为1时,那么这个猴子就是最后留下来的猴子(在总数为1时,该猴子的编号为0),只要依次向上一轮求该猴子的编号,就可求出这个猴子在总数为n时的编号,那么该编号就是最终结果。

int r=0;//即总数为1时,该猴子的编号为0
 // k表示猴子的总数,当k为n时,即表示第一轮,n-1表示第二轮,···,1表示最后一轮
for k from 2 to n
	r=(r+m)%k;
cout<<(r+1)<<endl;
  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值