猴子选大王(约瑟夫问题)

例题描述
由M只猴子围成一圈,从1到M进行编号,打算从中选出一个大王,经过协商,决定选出大王的规则:从第一个开始循环报数,
数到N的猴子出圈,下一个猴子从1开始报数。
输入样例
3 2
输出样例
3
方法一:
//模拟法

#include<iostream>
using namespace std;
#define MAX 100
long a[MAX+1]long m,n,i,Count,k;
int main()
{
	cin>>m>>n;
	for(i=1;i<=m;i++)
		a[i]=1;
	k=m;			//报数前,指向该数的前一只猴子
	for(i=1;i<=m;i++)//出圈M只猴子
	{
		Count=0;		//报数前,个数为0
		while(Count<n)//当前报数<n,继续报数
		{
			if(k==m)//从最后一个位置移动到第一个位置
				k==1;
			else
				k++;
			if(a[k]==1)//当前猴子在圈内,报数
				Count++;
		}
		a[k]=0;//猴子K出圈
	}
	cout<<k<<endl;
	return 0;
}

分析:上程序发现:当猴子出圈后,程序也会一一检查是否出圈,在这里花了大量的时间。
方法二:利用数组存放下一个该报数的编号,以8只猴子为例,a[1]的值为2,意思是指2号猴子,
如下所示

a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8]
2 3 4 5 6 7 8 1

这样构成了一个环,如果5号猴子出圈就用语句a[4]=a[a[4]],以后报到4号猴子时,就直接指向6号猴子。

#include<iostream>
using namespace std;
#define N 100000
long a[MAX+1]
long m,n,i,Count,k;
int main()
{
	cin>>m>>n;
	for(i=1;i<=m-1;i++)
		a[i]=i+1;
	a[m]=1;//最后一只猴子指向第一只
	k=m;//报数前,指向该报数的前一只猴子
	for(i=1;i<=m;i++)//出圈M只猴子
	{
		for(Count=1;Count<=n-1;Count++)//只需报数n-1次,a[k]指向该出圈瘊子
			k=a[k];
		a[k]=a[a[k]];
	}
	cout<<k<<endl;//最后一只出圈的是大王
}

为了更好的理解我列出来第一次小循环后的数组值:

a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8]
3 3 4 5 6 7 8 1

所以每次点名到1的时候回跳过二号值,外循环进行到M次时要出去的k值即为大王的位置。

方法三:
倒退算法:如果我们知道这个子问题的答案:设X是猴王,那么根据对应关系就有以下的对应关系,F[i]=(F[i-1]+k)%i;(k为每次需要喊的次数)

//倒退算法
#include<iostream>
using namespacestd;
int main()
{
	int i,m,k,ans;
	cin>>m>>k;
	ans=0;
	for(i=2;i<m=;i++)
		ans=(ans+k)%i;
	cout<<k+1<<endl;//因为这里的位次是从0开始的k%k=0.
}

猜你喜欢

转载自blog.csdn.net/weixin_43843978/article/details/88093619