约瑟夫问题:一个旅行社要从N个旅客中选出一名旅客,为他提供免费的环球旅行服务。旅行社安排这些旅客围成一个圆圈,从帽子中取出一张纸条,上面写着正整数m和n,其中m作为报数值,n表示第n旅客开始进行报数。游戏进行时,报到m时停止报数,报m的旅客被淘汰,然后再从他顺时针方向的下一个人开始重新报数,如此下去,直到只剩下一名旅客,这个幸存者就是游戏的胜利者,将得到免费环球旅行的奖励。
这里我们令N=8,m=3,n=1,请依次给出被淘汰者的编号和最终幸存者标号。
思维步骤如下:
- 先建立含有九个元素的数组arr[9],将arr[0]闲置,其余的arr[1]到arr[8]依次对应每一个旅客,便于理解。
- 将数组所有元素赋初值为0,表示未被淘汰;将计数变量cnt赋初值为1。
- 创建嵌套循环。(作者最开始只创建了里面的一个循环,结果发现只能淘汰一轮,无法继续淘汰,因此又创建了一个外层循环,确保每淘汰一轮且幸存者还未出现时,可以继续下一轮淘汰。)
- 由于我们将初始值全部赋值为0,而被淘汰者赋值为1,所以在每一轮淘汰之前先判断该编号所对应的旅客是否已经被淘汰了,即,所对应的值是否为1。
- 在确定未被淘汰后继续判定,若此时计数变量cnt为3,表示正好需要淘汰该编号所对应的旅客,此时将其赋值为1,表示已淘汰,输出其编号i,同时将计数变量cnt重置为0,准备进行下一轮计数。
- 再对此时的数组元素进行判定,求编号为1到8的元素是否和为7(这里作者好像多写了个arr[0],其实是没必要的。抱歉抱歉。),即,如果最终幸存者出现,则其他编号对应的数组元素已经被全部赋值为1了,而幸存者对应的元素还是0。
- 输出最终幸存者,并跳出所有循环,结束代码。(这里值得一提的是,作者最开始只使用了一个break,结果发现只能跳出一层循环,输出结果并不理想,多加了几个break之后又无法输出正确结果了,于是嫌麻烦的作者直接使用了goto+mark命令,一键跳出所有循环。众所周知,著名作家鲁迅说过,你觉得沙比的操作一定有简单的操作,只是你还没有发现。)
好了到这里我们的问题就全部解决了。
作者目前还是一个编程萌新,如果有出现错误的地方,希望各位大佬不吝赐教 :)
#include<iostream>
using namespace std;
int main()
{
int arr[9] = { 0 }; //闲置a[0],1到8对应八个人,先均赋值为0,表示均未被淘汰。
int cnt = 1; //计数,达到3则重置为1,并将此时的元素赋值为1,表示被淘汰。
cout << "被淘汰者序号依次为:";
for (int j = 1; j < 9; j++)
{
for (int i = 1; i < 9; i++)
{
if(arr[i]!=1) //判断是否已经被淘汰了
{
if (cnt == 3)
{
arr[i] = 1; //赋值为1,表示被淘汰。
cout << i << " "; //输出被淘汰的人员序号
cnt = 0; //重置为0,重新开始计数。
}
cnt++;
if (arr[0] + arr[1] + arr[2] + arr[3] + arr[4] + arr[5] + arr[6] + arr[7] + arr[8] == 7) //判断是否只剩最后一个人了
{
for (int k = 1; k < 9; k++)
{
if (arr[k] == 0) //判断幸存者
{
cout << endl <<"幸存者序号为:"<< k << endl;
goto mark; //break只能跳出一层程序,这里我们使用goto和mark,直接跳到mark后面执行程序,一键跳出所有循环。
}
}
}
}
}
}
mark:
system("pause");
return 0;
}