【案例】约瑟夫环问题
int main()
{
Josephus man[N]; init_Josephus(man,N);
baoshu(man, N, M) ;
printf("\n按座位顺序排列:\n"); display_Josephus(man,N); sort_by_count(man,N); printf("\n按出列顺序排列:\n"); display_Josephus(man,N); getch(); return 0;
模拟循环报数
i=0; while(counter<=N) //在N个人中模拟循环报数 { do{ pos=(pos+1) % N; //求余,进行环状处理 if( man[pos].count==0) //若编号pos还未出列 i++; //报数 if(i==M) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos].count =counter; //保存出列序号 counter++; }
修改
void void void void baoshu(Josephus *, int n, init_Josephus(Josephus *, display_Josephus(Josephus sort_by_count(Josephus *, int m); int n); *, int n); int n);
方法1:使用一维数组
• 用一个一维整型数组保存约瑟夫环
• 数组元素的序号(下标)表示人员的座位序号
• 数组元素的值表示出列的序号 如,man[0]=14 表示排在第1个位置的人将是第14个出列的人
#define N 41 #define M 3 int int int int
//总人数 //数到3出列
// 模拟报数 void baoshu(Josephus man[N], int n, int m) { int counter=1; //出列记数器 int i=0; //报数记数器 int pos=-1; //位置记数器 while(counter<=n) //在N个人中模拟循环报数 { do{ pos=(pos+1) % n; //求余,进行环状处理 if(man[pos].count==0) //若编号pos还未出列 i++; //报数 if(i==m) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos].count=counter; //保存出列序号 counter++; } }析
什么是约瑟夫环问题
• 传说,著名犹大历史学家Josphus曾讲过一个故事: 在罗马人占领乔塔帕特后,40个犹太人与Josphus躲到 一个洞中。40个犹大人决定宁愿死也不要被敌人逮到,于是 决定了一个自杀方式:41个人排成一个圆圈,由第1个人开 始报数,每报数到3,该人就必须自杀,然后再由下一个人 重新报数,直到所有人都自杀身亡为止。
// 初始化 void init_Josephus(Josephus man[N], int n) { int i; for(i=0;i<n;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; //保存出列的序号,为0表示未出列 } } void init_Josephus( Josephus *p, int n) 修改 { int i; for(i=0;i<n;i++,p++) { p->num = i+1; gets(p->name); p->count = 0; //保存出列的序号,为0表示未出列 } }
}
方法3:结构体指针
结构体指针作为各函数的形参
void void void void baoshu(Josephus man[], int n, int m); init_Josephus(Josephus man[], int n); display_Josephus(Josephus man[], int n); sort_by_count(Josephus man[], int n);
按出列顺序排序
void sort_by_count(Josephus man[], int n) { int i,j; Josephus t; for(i=0; i<n-1; i++) { for(j=i+1; j<n; j++) { if(man[i].count > man[j].count ) { t=man[i],man[i]=man[j],man[j]=t; } } } }
各项功能分别用函数实现
// 模拟报数 void baoshu(Josephus man[], int n, int m);
// 初始化 void init_Josephus(Josephus man[], int n);
// 输出结构体数组 void display_Josephus(Josephus man[], int n); // 按出列顺序排序 void sort_by_count(Josephus man[], int n);
模拟循环报数
man[N]={0}; //保存出列的序号,为0表示未出列 counter=1; //出列记数器 i=0; //报数记数器 pos=-1; //位置记数器
while(counter<=N) //在N个人中模拟循环报数 { do{ pos=(pos+1) % N; //求余,进行环状处理 if(man[pos]==0) //若编号pos还未出列 i++; //报数 if(i==M) //报数M的人 { i=0; //初始化记数器,又从1开始报数 break; } }while(1); man[pos]=counter; //保存出列序号 counter++; }
• 排名:根据选手的最后得分排出名次, • 查询:可查询选手信息(按姓名、出场顺序编号或最后名次 等)功能
• 具体功能设计不限于以上。
方法2:结构体数组
如果,还要知道参加游戏人的名字,怎么办? • 座位号:结构体成员(整型) 或 数组的下标 • 名字:结构体成员(字符串) • 出列序号:结构体成员(整型)
// 定义结构体类型 typedef struct { int num; // 座位序号 char name[20]; int count; // 出列序号 }Josephus; // 定义结构体数组 Josephus man[N]; // 结构体数组元素的初始化 for(i=0;i<N;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; }
然而,Josphus并不想遵从自杀,于是他先假装同意该 方案,然后坐到大家围成圆圈的第31个位置,逃过了这场死 亡游戏。
问:如何确定坐在哪个位置上可以逃脱呢?
问题分析
• 将41人排成一个圆圈,并编好序号,如图所示(内圈是座 位序号,外圈是自杀顺序(即,每个报到3的顺序)。
23 14 36 1 38 29 13 15 1 2 3 40 41 4 33 2 39 5 38 6 22 24 37 7 30 12 8 36 9 3 39 35 10 16 28 34 11 34 11 33 12 4 21 32 13 25 31 14 30 17 10 15 29 5 32 16 28 20 40 17 27 26 18 9 19 31 25 20 24 23 27 21 22 6 35 18 8 19 37 7 26
// 初始化 void init_Josephus(Josephus man[N], int n) { int i; for(i=0;i<n;i++) { man[i].num=i+1; gets(man[i].name); man[i].count=0; //保存出列的序号,为0表示未出列 } } // 输出结构体数组 void display_Josephus(Josephus man[], int n) { int i; printf("约瑟夫排列(最初位置--姓名--约瑟夫环位置):\n"); for(i=0;i<N;i++) { printf("%d--%s--%d\n",man[i].num ,man[i].name,man[i].count ); } }
大作业:歌手大奖赛竞赛系统
已知某大奖赛有n个选手参赛,m(m>2)个评委为参赛选手 评分(最高100分,最低0分)。 要求编程实现: • 出场顺序:随机从一位选手编号开始,按约瑟夫环的方式,3 人循环报数,决定选手的出场顺序。
• 评委评分:去掉1个最高分和1个最低分后,取平均分作为该 选手的最后得分。