C 语言实例 - 约瑟夫生者死者小游戏
30 个人在一条船上,超载,需要 15 人下船。
于是人们排成一队,排队的位置即为他们的编号。
报数,从 1 开始,数到 9 的人下船。
如此循环,直到船上仅剩 15 人为止,问都有哪些编号的人下船了呢?
实例
#include<stdio.h>
int c = 0;
int i = 1;
int j = 0;
int a[30] = { 0 };
int b[30] = { 0 };
int main() {
while (i<=31) {
if (i == 31) {
i = 1;
} else if (c == 15) {
break;
} else {
if (b[i] != 0) {
i++;
continue;
} else {
j++;
if (j != 9) {
i++;
continue;
} else {
b[i] = 1;
a[i] = j;
j = 0;
printf("第%d号下船了\n", i);
i++;
c++;
}
}
}
}
}
执行以上实例,输出结果为:
第9号下船了 第18号下船了 第27号下船了 第6号下船了 第16号下船了 第26号下船了 第7号下船了 第19号下船了 第30号下船了 第12号下船了 第24号下船了 第8号下船了 第22号下船了 第5号下船了 第23号下船了
C 语言实例
袁博
cdy***98@163.com
我认为用一个数组完成这个算法更好理解。
#include<stdio.h> int main(){ int count=0,//记录下船的人数 ,到15就终止程序 a[30]={0},//储存30人信息,0代表在船上,1代表下船了 i=0,//循环索引 c=0;// 记录报数号码,到9就清零,由下一位重新报数 while(1) { if(a[i]==0)c++;//记录报数号码 if(c==9){ count++;//下船人数加一 a[i]=1; //标记这个人下船了 printf("第%d号下船了。\n",i+1); c=0;//到9就清零,由下一位从0重新报数 } if(count==15)break;//下船的人数到15就终止程序 i++;//分析下一个人 if(i==30)i=0; //将数组变成一个圈,循环往复 } return 0; }袁博
cdy***98@163.com
lsz
257***7596@qq.com
#include <stdio.h> int main() { int arr[30]; //创建30的数组,值全部为1 for(int i = 0;i<30;i++){ arr[i]=1; } int nowP = -1; //当前报出的数,-1 开始因为 while 一开始就要 ++ int callTime = 0; //当前共有几个人下船,即完成了几次0-8(1-9)的报数 int realP = -1; //当前报数的人的真是位置 while(callTime!=15){ //只要下船人数没达到15 即一只报数 realP++; //开始记录当前哪个位置在报 if(realP == 30)realP = 0; //如果当前该坐标为30的人,即第"31"个人报,马上切换到第0个人开始报 if(arr[realP] != 0)nowP++; //如果当前人的状态不是0,即仍在船上,就记录一次报数。 if(nowP == 8){ //如果报到的数字为8,即第九人,则下船人数+1 callTime++; arr[realP] = 0; //1改为0,标记为已下船 nowP = -1; //重置nowP,下一个人继续从0报 printf("Number %d is out.\n",realP+1); //输出当前下船的人 } } return 0; }lsz
257***7596@qq.com
zhaojingjing
jzh***upt@163.com
用一种比较复杂的方式解决。
前面的用的方法都比较简单,可惜我没想到,我一上来就想的是用循环链表。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> typedef struct num{ int num; struct num * next; }student ; int main() { int i,j; student* head = NULL; student* tail = NULL ; student* p = NULL; student* q = NULL; student *temp; bool ishead = true; for(i=1;i<31;i++) { student *people = malloc(sizeof(student)); people->num=i; if(ishead){ head = people; head ->next = NULL; tail = people; ishead = false; } else { tail->next = people; tail = people; } } if(NULL!=tail || NULL == tail) tail ->next = head; i=30; p=head; q=head; while(i>15) { for(j=1;j<10;j++) { //printf(" now the num %d \n",p->num); if(j==9) { printf("the num %d get off \n",p->num); i--; q->next = p->next; free(p); p=q->next; } else{ q=p; p=p->next; } } } return 0; }zhaojingjing
jzh***upt@163.com
starrysky
847***051@qq.com
先确定哪个位置的人会报出 9,找到之后下一次跳过此人,直至人数等于 15。
#include<stdio.h> int main() { int i= 1; //绝对位置参数,代表当前的真实位置 int size = 30; //总人数 int b = 1; //当前的报数,或者说相对位置,每循环一次都加1,至多为9 int flag[30]; //记录数组,记录当前位置是否已选,已选记为1 for(int j = 0 ; j < 30 ; j++) { flag[j] = 0; //记录数组初始化为0 } int num[30]; //位置数组 for(int j = 0 ; j < 30 ; j++) { num[j] = j+1; //位置数组初始化 } while(size >15) //15人下船,因此人数大于15时才执行 { if(i == 31) //30即为最后一个人,下一轮要从头开始,因此30的下一个31实际是第1 i = 1; if(b == 10) //以9为循环,报数到9,下一个10改报1 b = 1; if(flag[i] == 1) //判断之前是否已经报过9 { b--; //跳过之前被选的人 //举例说明:第一轮筛选,9这个位置被选了,并通过flag[8]记录9这里已选。在第二轮筛选时就需要跳过9这里,假设第二轮第8个位置报数是1,第9个位置跳过,则第10个位置的报数为2。 //而每次循环b都自加,所以9这里b自减,保证位置10的b值正确,其他位置同理 } if(b == 9) //若当前报数为9 { flag[i] = 1; //标记此人已经报出过9 printf("%d\n" , num[i-1]); //打印报数为 9 的人的绝对位置 size--; //找到一个人,总人数减1 } i++; //绝对位置自加 b++; //报数自加 } }starrysky
847***051@qq.com
夜雨无愁
295***5188@qq.com
参考方法:
#include <stdio.h> #include <stdlib.h> #define ss 30 //定义总人数 #define sw 15 //定义下船人数 #define gz 9 //定义下船规则 #define bz (ss*10) //定义下船标志 int main() { int * arr_ss; int k = -1; arr_ss = (int *)malloc(ss * sizeof(int));//创建数组储存人数编号 for(int i = 0;i < ss;i++)//对人数进行编号 { arr_ss[i] = i + 1; } for(char n = 0;n < sw;n++)//循环sw次 { for(char m = 0; m < gz;m++)//循环gz次 { if(k < ss - 1) //令其循环报数 k++; //为什么是ss-1 else //因为k < ss会在k = 30时将k复位为0,而数组只有29 k = 0; //所以 k < ss - 1才会在k = 29时将k复位为0 if(arr_ss[k] > bz) { //如果当前编号已经被标志, m--; //那么我们接着下一个编号报数 } } printf("%d号下船\n",arr_ss[k]); arr_ss[k] += bz;//标志已经下船的编号 } }夜雨无愁
295***5188@qq.com
big_tree
286***4460@qq.com
上面都是大佬,在没看笔记之前,自己也按照自己的思路写了一下。
思路:就像把30张扑克牌叠在一起,抽到了,就丢掉。难点在于如何将收尾衔接好
#include <stdio.h> int main() { int i=0,j=1,m=0; int a[31] = { 0 }; for (m=0;m<15;m++){ for (;i<9;){ if(a[j]==0){ j++; i++; } else j++; if (j==31) j=1; } if ((j-1)==0){ printf("第30号下船\n"); a[30]=1; j=1; } else{ printf("第%d号下船了\n",j-1); a[j-1]=1; } i=0; } return 0; }big_tree
286***4460@qq.com
Justin
576***135@qq.com
参考:
#include <stdio.h> int main() { int nCount = 0; //记录下船人数,到15终止 int arr[30] = {0};//船上人数组,0表示在船上,1表示下船了 int i = 0;//循环索引 int c = 0;//从1-9报数的号码 while ( nCount < 15 ) { i++; if (i == 31) { i = 1; } if ( arr[i] != 1 )//没有下船的人才参与报数 { c++; } if ( c == 9 )//报到9的人下船,下一个人重新报数 { nCount++; c = 0; arr[i] = 1; printf("\n---->%d:第%d号下船\n", nCount, i); } } return 0; }Justin
576***135@qq.com
13579
946***726@qq.com
看了一遍都是用标记的,为什么没有这种解法呢?
#include<stdio.h> int main() { int man[30],i,j=0,l=1; for(i=0;i<30;i++)(man[i]=l++); int *hum; int k=0,die[15]; while(k<15) { hum=man; for(i=0;i<30-k;hum++) { if(++j==9) { die[k]=*hum; k++; j=0; } else { man[i]=*hum; i++; } } } for(i=0;i<15;i++)(printf("%d\t",die[i])); }13579
946***726@qq.com
大宅院里的三表哥
gul***r@outlook.com
使用数组:
#include <stdio.h> #define LIVES 30 // 船上的初始人数 #define DEADS 15 // 需要下船的人数 #define DEAD_NUM 9 // 数到该数下船 int main() { static int died[LIVES]; // 0代表在船上,1代表下船了 int i, j, cur=0; // cur表示当前报数的人的编号-1 for(i=0; i<DEADS; i++) // 每次循环下船一个人 { for(j=0; j<DEAD_NUM; j+=1-died[cur++]) // 开始一轮报数 if(cur == LIVES) cur = 0; // 最后一个人报完则又从第一个人开始 printf("第%d号下船了\n", cur); died[cur-1] = 1; // 记录下船信息 } return 0; }使用链表:
#include <stdio.h> #define LIVES 30 // 船上的初始人数 #define DEADS 15 // 需要下船的人数 #define DEAD_NUM 9 // 数到该数下船 struct person { unsigned id; struct person *next; } persons[LIVES], *p, *before; int main() { int i, j; // 初始化循环链表 for(i=0; i<LIVES; i++) { persons[i].id = i + 1; // 从1开始给船上的人编号 persons[i].next = persons + i + 1; } persons[LIVES-1].next = persons; p = persons + LIVES - 1; // 计数开始时会忽略第一个人,故设置初始值为最后一个人 for(i=0; i<DEADS; i++) { // 报数 for(j=0; j<DEAD_NUM-1; j++) p = p->next; // 使before指向下船人的前一个人 before = p, p = p->next; printf("第%u号下船了\n", p->id); // 将下船人的前一个人与后一个人建立前后关系 before->next = p->next; } return 0; }使用公式法递推:
#include <stdio.h> #define LIVES 30 // 船上的初始人数 #define DEADS 15 // 需要下船的人数 #define DEAD_NUM 9 // 数到该数下船 int main() { int i, j, id; // id表示下船的人的编号 for(i=0; i<DEADS; i++) // 每次循环下船一个人 { for(j=i, id=-1; j>=0; j--) id = (id + DEAD_NUM)%(LIVES - j); printf("第%d号下船了\n", id+1); } return 0; }大宅院里的三表哥
gul***r@outlook.com