问题描述
一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?
输入格式:
输入在一行中给一个正整数N(≤1000)。
输出格式:
在一行中输出当选猴王的编号。
输入样例:
11
输出样例:
7
数组模拟
使用标记法也就是数组模拟的方法,我自己起的名字哈哈。简单易懂使用一个mark数组来表示当前的这只猴子有没有数过最后一个数字(初始值均为0,表示没有数过,数过的赋值1),记录每一次循环位于队尾的猴子数的数字,合理给第一只猴子数的数赋值。
代码如下:
#include <iostream>
#include <stdio.h>
using namespace std;
// 0 1 2 3 4 5 6 7 8 9 10
int main()
{
bool mark[1001] = {0};//动态标记 未被选中过0 选中过1
int N = 0;//输入的猴子数
scanf("%d",&N);
int num = N;
//int currentNum = 1;//从1开始遍历
int cunt = 1;
while(num > 1){
for(int i = 0;i < N;++i){
bool selecting = 0;
//判断当前的猴子是没有被剔除圈的,才有资格报数
if(mark[i] ==0){
selecting = 1;
}
//选中的猴子 数到三的猴子
if(selecting&&(cunt == 3)){
mark[i]=1;
--num;
}
//数到最后的猴子
if(selecting){
if(cunt==3)
cunt=1;
else
++cunt;
//cunt = (++cunt)%3;
}
}
}
for(int j = 0;j < N;++j){
if(!mark[j]){
//printf("选出的猴子大王编号是%d",j+1);
printf("%d",j+1);
break;
}
}
return 0;
}
原本写的比较简单,再补充一个vector版的
vector数组实现
/*****************************
* effect:vector版猴子选大王
* author:wyrctzy
* environment:Code::Blocks 16.01
* time:2019/1/25 00:00
*****************************/
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int N;
vector<int>monkey;//选择序列
vector<int>::iterator item;//遍历指针
cin>>N;
for(int i=1;i<=N;i++)
monkey.push_back(i);//在vector尾部把当前的i值追加上
item=monkey.begin();
//剩下的最后一个就是king
while(monkey.size()>1)
{
for(int i=1;i<3;i++)
{
item++;
//到达vector尾部,重新从头开始
if(item==monkey.end())
item=monkey.begin();
}
//数3的直接从数组中删掉
item=monkey.erase(item);
//判断一下删除完成后指针的情况,是不是指在队尾
if(item==monkey.end())
item=monkey.begin();
}
//输出指针当前位置的值
cout<<*item<<endl;
return 0;
}
数组的基本就是这样了,还可以使用数学上的方法解决该问题。
先看一下约瑟夫问题的百度百科给出的相关解释
什么是约瑟夫问题–百度百科
数学方法解决
递推公式:
f[1]=0;
f[i]=(f[i-1]+m) mod i; (i>1)
代码如下:
#include <iostream>
using namespace std;
const int m = 3;
int main()
{
int n, f = 0;
cin >> n;
for (int i = 1; i <= n; i++)
f = (f + m) % i;
cout << f + 1 << endl;
}