约瑟夫环问题之例-----猴子选大王

        约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。

        猴子选大王只是约瑟夫问题衍生出的一个典型算法例题,本篇博文介绍两种解法:暴力法+回溯法。

        暴力法:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=30;
int main()
{
	int a[N];
	memset(a,0,sizeof(a));
	cout<<"此处有"<<N<<"只猴子!"<<endl; 
	for(int i=0;i<N;++i)
	{
		a[i]=i+1;
	}
	for(int i=0;i<N;++i)
	{
		if(i%5==0)
		{
			cout<<endl;
		}
		printf("%3d ",a[i]);
	}
	int getout;
	cout<<endl<<"请输入出队的序号:"; 
	cin>>getout;
	int current=0;
	for(int i=N;i>0;--i)
	{
		current=(current+getout-1)%i;//这里是关键,每趟出队的序号
		printf("第%3d只猴子滚出去.\n",a[current]);
		for(int j=current;j<i-1;++j)//出队过后便前移元素
		{
			a[j]=a[j+1];
		} 
	}
	cout<<"猴王是第"<<a[current]<<"只猴子!"<<endl; 
	return 0;
}

大家可以看看结果:

约瑟夫环问题之例-----猴子选大王_第1张图片


            回溯法:

    得意都说回溯大法好,今天我们来瞅瞅!

#include<stdio.h>
#include<iostream>
using namespace std;
const int N=9;
int main() 
{
	int a[N];
	for(int i=0;i<N;++i)
	{
		a[i]=i+1;
	}
	for(int i=0;i<N;++i)
	{
		cout<<a[i]<<"    ";
	}
	cout<<endl;
	int index; 
    cout<<"输入每次要出队的序号呗,大爷:"; 
	cin>>index;
	for(int i=N-1;i>=0;--i)//小伙伴们,能看出来,算法的精髓吗?其实i大于0就行了,不过...懒得改了
	{
		for(int k=1;k<=index;++k)//每次出队时,把要出队的序号排在数组末尾
		{
			int temp=a[0];
			for(int j=0;j<i;++j)
			{
				a[j]=a[j+1];
			}
			a[i]=temp;
			for(int p=0;p<N;++p)//测试每轮数组位置的变动
			{
				cout<<a[p]<<"    ";
			}
			cout<<endl;
		}
	}
	for(int i=N-1;i>=0;--i)
	{
		cout<<"第"<<a[i]<<"只猴子滚出去!"<<endl; 
	}
	cout<<"猴王是"<<a[0]<<"只猴子!"<<endl; 
	return 0;
}
约瑟夫环问题之例-----猴子选大王_第2张图片

这题可算是熟悉约瑟夫环的一个比较好的例子了,对回溯也是很好的诠释。

再回顾一下百度百科对回溯的诠释:

回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

用回溯算法解决问题的一般步骤为:
1、定义一个解空间,它包含问题的解。
2、利用适于搜索的方法组织解空间。
3、利用深度优先法搜索解空间。
4、利用限界函数避免移动到不可能产生解的子空间。
问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。


你可能感兴趣的:(算法,回溯,暴力,经典问题算法)