题目描述
约瑟夫问题 (本题要求用循环链表实现)
约瑟夫问题是一个经典的问题。已知n个人(不妨分别以编号1,2,3,…,n 代表 )围坐在一张圆桌周围,从编号为 k 的人开始,从1开始顺时针报数1, 2, 3, ...,顺时针数到m 的那个人,出列并输出。然后从出列的下一个人开始,从1开始继续顺时针报数,数到m的那个人,出列并输出,…依此重复下去,直到圆桌周围的人全部出列。
输入:n, k, m
输出:按照出列的顺序依次输出出列人的编号,编号中间相隔一个空格,每10个编号为一行
非法输入的对应输出如下
a)
输入::n、k、m任一个小于1
输出:n,m,k must bigger than 0.
b)
输入:k>n
输出:k should not bigger than n.
例:
输入:9,3,2
输出:4 6 8 1 3 7 2 9 5
解题思路
这个题就是一个简单的模拟,大佬还有更简单的数组方法,已经在结尾部分大致说明了。
(然而我并不懂这个)还是最好按照题目要求使用循环链表(
由于我最初理解错了,我写出了双向循环链表:(which下一个题需要两个方向都数,就可以用)
2.3.Josephus_problem_bidirectional 双向约瑟夫问题_ttttttvc的博客-CSDN博客
struct p{
int id;
struct p *prior,*next;
};
struct p first,*cur,*ppre=&first,*pnex=(struct p*)malloc(sizeof(struct p));
first.id=1;
first.next=pnex;
if(k==1) cur=&first;
for(int i=1;i<n-1;i++)
{
pnex->id=i+1;
if(i+1==k)
{
cur=pnex;
}
pnex->prior=ppre;
pnex->next=(struct p*)malloc(sizeof(struct p));
ppre=pnex;
pnex=pnex->next;
}
pnex->id=n;
pnex->prior=ppre;
pnex->next=&first;
if(k==n) cur=pnex;
first.prior=pnex;
(这里ppre和pnex指向前后两个在使用的结构,并用cur来记录了第k个人的位置)
所以实际上只需要这样就行了:
struct p{
int id;
struct p *next;
};
struct p first,*cur,*pnex=(struct p*)malloc(sizeof(struct p));
first.id=1;
first.next=pnex;
if(k==1) cur=&first;
for(int i=1;i<n-1;i++)
{
pnex->id=i+1;
if(i+1==k)
{
cur=pnex;
}
pnex->next=(struct p*)malloc(sizeof(struct p));
pnex=pnex->next;
}
pnex->id=n;
pnex->next=&first;
if(k==n) cur=pnex;
接下来是一个过程模拟,应该非常简单
注意事项是:这里只需要走m-1步到达第m个人;具体实现上只能走m-2步,这样有利于删除该元素。另外一种想法是不用删除元素,直接使用一个结构体标记,每次到达已经出圈的人之后就跳过,但我觉得可能会增加运行时间
int cnt = 0;
while (cur->next != cur)
{
for (int j = 1; j < m - 1; j++)
{
cur = cur->next;
}
if (cnt == 9)
{
printf("%d\n", cur->next->id);
cnt = 0;
}
else
{
printf("%d ", cur->next->id);
cnt++;
}
cur->next = cur->next->next;
cur = cur->next;
}
printf("%d\n", cur->id);
然后还有一点是这个cnt,为了解决题目要求很烦人的一个点:10个一排,换行。然后最后一个元素总是要换行。
贴源代码
(实际上就是C,我的三脚猫功夫…)
#include<iostream>
#include<cstdlib>
using namespace std;
struct p {
int id;
struct p * next;
};
int main()
{
int n, k, m;
scanf_s("%d%*c%d%*c%d", &n, &k, &m);
if ((n < 1) || (k < 1) || (m < 1))
{
cout << "n,m,k must bigger than 0.\n";
return 0;
}
if (k > n)
{
cout << "k should not bigger than n.\n";
return 0;
}
//两种特例
struct p first, * cur=NULL, * pnex = (struct p*)malloc(sizeof(struct p));
first.id = 1;
first.next = pnex;
if (k == 1) cur = &first;
for (int i = 1; i < n - 1; i++)
{
pnex->id = i + 1;
if (i + 1 == k)
{
cur = pnex;
}
pnex->next = (struct p*)malloc(sizeof(struct p));
pnex = pnex->next;
}
pnex->id = n;
pnex->next = &first;
if (k == n) cur = pnex;
/*cur=&first;
for(int w=0;w<n+1;w++)
{
printf("%d\n",cur->id);
cur=cur->next;
}用于链表构成之后检验*/
int cnt = 0;
while (cur->next != cur)
{
for (int j = 1; j < m - 1; j++)
{
cur = cur->next;
}
if (cnt == 9)//应该换行了
{
printf("%d\n", cur->next->id);
cnt = 0;
}
else
{
printf("%d ", cur->next->id);
cnt++;
}
cur->next = cur->next->next;
cur = cur->next;
}
printf("%d\n", cur->id);
return 0;
}
继续学习
下面拓展一下约瑟夫出圈的黑暗故事……
据说着名犹太历史学家 Josephus有过以下的故事。
在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人找到,于是决定了一个自杀方式:41个人排成一个圆圈,由第1个人 开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
这里用例就是41,1,3…是不是突然觉得有些难过了…为纪念这个故事我把下面的测试结果放出来:
然后我们讨论对于单向的Josephus_problem的最简单方法:数组法
其实一个最简单且不用list实现的方式就是利用(下标+1)%n,当然也是经常能接触到的东西(但我怎么就不会用),所以其实可以先利用循环,用count来记录出圈的是第几个人,然后一个int position来记录到达的位置
应该非常容易实现,所以我决定马上手撕(锻炼一下我可怜的编程能力啦)
int used[n] = {0};
int position = k;
for (int count = 0; count < n; count++) {
for (int i = 0; i < m - 1; i++) {
position = (position + 1) % n;
while(used[position]) {
position = (position + 1) % n;//当前已出圈时,移动到下一个还未出圈的
}
}//m次报数,m-1次移动
if (0 == position)
printf("%d,", n);
else
printf("%d,", position);
used[position] = 1;
do {
position = (position + 1) % n;
} while (used[position]);
//首先到下一个,如果下一个已经出圈,移动到下一个还未出圈的
}
——2022.9.22