约瑟夫问题本身并不难,但求解的方法很多;题目的变化形式也很多,这里介绍一个用链表来实现的方法。
题目中30个人围成一圈,因而启发我们用一个循环链来表示。可以使用结构数组构成一个环形链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的 链;其二为该人是否被扔下海的标记,为1表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。
算法实现如下:
#include <stdio.h>
struct node{ int nextp; //指向下一个人(数组元素下标) int no_out; //是否被扔下海的标记:没有被扔下海,0:已被扔下海 }link[31]; //30个人,0号元素没有使用
void main(){ int i,j,k; printf("The original circle is(+:pagandom,@christian):/n"); for(i=1;i<=30;i++){ //初始化结构数组 link[i].nextp=i+1; //指针指向下一个人(数组元素下标) link[i].no_out=1; //标志置为1,表示人都在船上 } link[30].nextp=1; //第30个人的指针指向第1个人以构成环 j=30; //j:指向已经处理完毕的数组元素,从link[j]指向的人开始计数 for(i=0;i<15;i++){ //i:已扔下海的人数计数器 for(k=0;;) //k:决定哪个人被扔下海的计数器 if(k<9){ //计数不到9则继续计数 j=link[j].nextp; //修改指针,取下一个人 k+=link[j].no_out; //进行计数。因已扔下海的人标记为0,故这样计数不会影响正常结果 } else break; //计数到9则停止计数 link[j].no_out=0; //将标记置0,表示该人被扔下海 } for(i=1;i<=30;i++) printf("%c",link[i].no_out?'@':'+'); //+:被扔下海,@:在船上 printf("/n"); }
计算机等级考试三级的C语言上机题里有一道比较有难度的题:这就是“出圈”题,完整题目如下:
设有n个人围坐一圈并按顺时针方向从1到n编号,从第s个人开始进行1到m的报数, 报数到第m个人,此人出圈,再从他的下一个人重新开始1到m的报数,如此进行下去直到所有的人都出 圈为止。现要求按出圈次序,每10人一组,给出这n个人的顺序表。 请考生编制函数Josegh()实现此功能并调用函数WriteDat()把结果p输出 到文件JOSE.OUT中。
这里给出另一种算法,分析: 设 n = 100, s = 1,m = 10。 (1) 将1到n个人的序号存入一维数组p中; (2) 若第i个人报数后出圈,则将p[i]置于数组的倒数第i个位置上,而原来第i+1个至倒数第i个 元素依次向前移动一个位置; (3) 重复第(2)步直至圈中只剩下p[1]为止。 注意:部分源程序存放在文件prog1.c中。 请勿改动主函数main()和输出数据函数WriteDat()的内容。 #include <stdio.h> #define N 100 #define S 1 #define M 10 int p[100], n, s, m ; void WriteDat(void) ; void Josegh(void){ int i,j,s1,w; s1=s; for(i=1; i<=n; i++) p[i-1]=i; /*数组p依次存放n个人的原始位置,在每次报数离圈后,移动数组p中元素,如图,最终p的逆序 为小孩出圈的次序*/ for(i=n; i>=2; i--){ //从圈子有n个人到只剩下2个人 s1=(s1+m-1)%i; //出圈人的编号是(s1+m-1)%i if(s1==0) s1=i; //要开始报数的是最后一个人 w=p[s1-1]; //将要出圈的人的原始编号先赋给w for(j=s1; j<=i-1; j++) p[j-1]=p[j]; //离圈人后的数组各项依次向前移一位,覆盖离圈人位置 p[i-1]=w; //将要出圈的人放在后面腾出的位置,(考虑到WriteDat函数输出时对存放离圈顺序编号的p[]按从末至首读取) } } void main() { m = M ; n = N ; s = S ; Josegh() ; WriteDat() ; } void WriteDat(void) { int i ; FILE *fp ; fp = fopen("jose.out", "w") ; for(i = N - 1 ; i >= 0 ; i--) { // printf("M ", p[i]) ; fprintf(fp, "M", p[i]) ; if(i % 10 == 0) { printf("/n") ; fprintf(fp, "/n") ; } } fclose(fp) ; }
本算法示意图:联系本人
用具体数字提出该出圈问题,并用C++语法描述:
10个小孩围成一圈玩游戏,每个小孩的编号依次为1,2,3,3,4,... ,10。从第1个小孩开始,顺时针数3个小孩,该小孩离开圈子,接着再数3个小孩,该小孩又离开圈子,最后剩下的一个小孩为胜利者。编程输出离开圈子小孩 的顺序和胜利者的编号。
分析:利用一维数组编写程序,每个元素表示一个小孩,元素的值为小孩的编号。离开圈子的小孩对应的元素为0.这里有一个关键问题需要解决:如何用一维数组 表示小孩围成一圈?或者说,当数到数组尾时如何自动回到数组首?解决此问题可以采用“加1求余”法,即将元素的下标改为下标加1除以人数的余数:i= (i+1),便可解决围成一圈的问题。
程序如下:
#include<iostream.h> void main() { int a[10]; for(int i=0;i<10;i++) //给每个小孩一个编号 a[i]=i+1; int k=1; //标识处理第k个离开的小孩 i=-1; //下标初值为-1,下一个值0就是第1个小孩的下标 cout<<"离开圈子的小孩依次是:"; while(1) { for(int j=0;j<3;) //在圈中数3个小孩 { i=(i+1); if(a[i]!=0) //如果该小孩在圈中,则数数有效 j++; } if(k==10)break; cout<<a[i]<<" "; //输出离开圈子的小孩编号 a[i]=0; //标识该小孩已离开 k++; } cout<<endl<<"胜利者是:"<<a[i]<<endl; //输出胜利者编号 }
运行情况: 离开圈子的小孩依次是:3 6 9 2 7 1 8 5 10 胜利者是:4
当然,你可以该程序,使其具有通用性。例如,总人数可以改变,每次数到第几个人离开圈子也可以改变。
http://topic.csdn.net/t/20061027/17/5114598.html