Josephus 问题:
一群小孩围成一个圈,任意假定一个数 m,从第一个小孩起,顺时针方向数,每数到第 m 个小孩时,该小孩便离开。小孩不断离开,圈子不断缩小,最后剩下的一个小孩便是胜利者。究竟胜利的是第几个小孩呢?
案例分析:
解答这个问题,首先要定义一个数组,其元素个数就是小孩个数。必须预先设置一个小孩个数常量,以便定义一个数组。
对每个小孩赋以一个序号作为小孩的标志。由于数组是局部作用域的,一旦分配之后,就删不去,得等到作用域结束才会自动抹去,所以当小孩离开时,只能修改该数组元素值来表示小孩的离开。
数组是线性排列的,小孩是围成圈的,用数组表示小孩围成的圈,要有一种从数组尾部跳到数组头部的技巧,这就是“加1取模”。当数到数组尾的时候,下一个数组下标值可以算得为0,从而回到数组首以继续整个过程。程序如下:
#include "stdafx.h" #include<iostream> #include<iomanip> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { //建立小孩数组 const int num = 10; //小孩个数 int interval; //每次数 interval 个小孩,便让该小孩离开 int a[num]; //小孩数组 //给小孩编号 for(int i=0; i<num; i++) //小孩的编号只与小孩的个数有关 a[i]=i+1; //输入小孩间隔 interval cout<<"please input the interval:"; cin>>interval; //将全体参加的小孩输入 for(int i=0; i<num; i++) //顺序输出开始时的小孩编号 cout<<a[i]<<","; cout<<endl; int k=1; //表示处理第 k 个离开的小孩 int i=-1; //数组下标(下一个值 0 就是第一个小孩的下标) //处理获胜前的小孩 while(true) { //在圈中数 interval 个小孩 for(int j=0; j<interval; ) { i=(i+1) % num; //对下标加 1 取模(使得最后和一个小孩的下一个是第一个小孩) if(a[i] != 0) //如果该小孩在圈中,则承认该数有效 j++; } if(k == num) break; //该小孩是最后的胜利者 cout<<a[i]<<","; //输出离开的小孩编号 a[i] = 0; //表示该小孩已经离开 k++; //准备处理下一个圈中的小孩 } //break 语句跳转到此 cout<<endl<<"No."<<a[i]<<"boy's won."<<endl; return (0); }
原文地址:《Visual C++ 编程从基础到应用》 第四章末尾案例