1、需求分析
1.1、问题阐述
约瑟夫(Joseph)问题的一种描述是:编号为1.2...n的n个人按顺时针方向圈坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第-一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值.从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人.全部出列为止。试设计一个程序求出出列顺序。
1.2、基本需求
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
2、概要设计
利用循环链表来代表约瑟夫环,每次出列的人从链表中删去,一直循环到最终剩下一人。
3、测试数据
m的初值为20;n=7,7个人的密码依次为:3,1.7,2,4,8,4,首先m的值是6 (正确的出列顺序为6,1,4,7,2,3,5)
4、详细设计
首先创建一个空的循环链表,然后根据输入的总人数n和循环数m来为循环链表动态分配空间,并利用该循环链表来代表我们的约瑟夫环,对于每次计数循环出列的人,只需将其代表的结点从循环链表中删去然后将出列的人的密码作为新的m值,随后一直循环到最终剩下一个结点即剩下最后一个人。
5、用户手册
只需要在开头输入总人数n和每次出列所需人数m即可计算出最终优胜者。
6、测试结果
测试结果完全正确:
7、源代码:
//
// Created by stu_kk on 2022/4/25.
//
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef struct List{//链表
int key;
int password;
struct List *next;
}list;
int n,m;
list *ans = NULL;
int pass[1001];
void add(){
list *head,*body,*tail;
head = (list*)malloc(sizeof(list));
head->key = 1;
head->password = pass[0];//赋值密码
tail = head;
for(int i = 2;i<=n;i++){
body = (list*)malloc(sizeof(list));
body->key = i;
body->password = pass[i-1];
tail->next = body;
body->next = NULL;
tail = body;
}
tail->next = head;//头尾链接,循环链表
ans = head;
}
void solve(){
int size = n;
list *p = ans;
list *parent = ans;//设置它为p的上一个指针.
while(parent->next!=ans){
parent = parent->next;
}
printf("依次离开的人:\n") ;
while(p->next!=p){
for(int i = 1;i<m;i++){//数到m的人离开
parent = p;
p = p->next;
}
printf("%d ",p->key);
m = p->password;
parent->next = p->next;
free(p);
p = parent->next;
}
printf("%d",p->key);
}
int main(){
printf("请输入人数n和数值m :");
scanf("%d%d",&n,&m);//n个人、每数到m出列 。
printf("请输入%d个人的密码 :",n);
for(int i = 0;i<n;i++){
scanf("%d",&pass[i]);
}
add();//先将n个人都加入链表
solve();
return 0;
}