约瑟夫环的三种解法

约瑟夫环的三种解法

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
N个人从1开始编号,问最后活下来的人的编号是多少。

方法1:数组模拟。

#include<bits/stdc++.h>
using namespace std;
int main(){
	 printf("--------约瑟夫环问题----------\n");
	 printf("请输入总共人数和报数死亡的编号数:");
	 int n,m;
	 cin>>n>>m;
	 bool a[n+1];
	 memset(a,0,sizeof(a));
	 int tot=0,cnt=0,i=0;//当前死亡总人数,报数的报数器.
	 while(cnt<n){
	 	i++;
	 	if(i>n) i=1;
	 	if(!a[i]) cnt++;
	 	if(cnt==m){
	 		cnt=0;
	 		tot++;
	 		a[i]=1;
	 		printf("第%d个死的人的编号为%d\n",tot,i);
		 }
	 }
	 printf("游戏结束,所有人死亡\n");
	 return 0;
} 

方法2:数学递推

主要是利用递推的思想,令Y[ i ]为有i个人时最后活着的人的编号(从0开始编号)。显然Y[ 1 ]=0,现在我们来考虑Y[2]为多少。我们已知报到 m-1 的会死.
所有当有两个人的时候.死的就是那个报m-1的人.然后编号为m的那个人就活了下来.所以Y[ 2 ] = Y[ 1 ] + m
依次进行递推:可得Y[ i ] = Y[ i-1] + m ,由于我们知道报数类似于一个循环.所以编号可能会超出范围.所以我们对每一个进行取模。 Y[ i ] = ( Y [ i- 1] + m) % i ,对 i 取模 保证 编号是从0到 i -1 。

这个递推关系可以这样思考:

y i = y i − 1 + m y_i = y_{i-1}+m yi=yi1+m

显然 y i y_i yi y i − 1 y_{i-1} yi1 多一个人。

y i y_i yi 死了一个人之后就变成了 y i − 1 y_{i-1} yi1这种i情况。

y i y_i yi 第一个死的人就是编号 m − 1 m-1 m1(从0开始)。
所以第二轮的起始下标就是 m m m。而 y i − 1 y_{i-1} yi1 的起始下标是 0 0 0
所以他们两个的关系就是 + m +m +m的关系。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,m;
	while(cin>>n>>m){
		int p=0;
		for(int i=2;i<=n;i++){
			p=(p+m)%i;// i=2时是n=2时最后活着的人编号(从0开始),i=1时,最后活着的人编号肯定为0 
		}
		printf("最后活着的人的编号为%d\n",p+1);
	}
} 

方法3:队列实现.

将队伍看成一个循环队列.如果没点到第m个人就放到队尾,否则弹出,cnt重置.下面上代码.

#include<bits/stdc++.h>
using namespace std;
int main(){
	queue<int>q;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) q.push(i);
	int cnt=1;
	while(q.size())
	{
		if(cnt==m)
		{
			printf(q.size()==1?"%d\n":"%d ",q.front());
			q.pop();
			cnt=1;
		}
		else {
				int tmp=q.front();
				cnt++;
				q.pop();
				q.push(tmp);
		 }
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值