题目描述

约瑟夫问题是一个经典的问题,我们不妨将这个经典问题进行扩展,变成一个双向的约瑟夫问题。

  已知 n 个人(不妨分别以编号 1,2,3,...,n 代表 )围坐在一张圆桌周围,首先从编号为 k 的人从 1 开始顺时针报数,1, 2, 3, ...,记下顺时针数到 m 的那个人,同时从编号为 k 的人开始逆时针报数,1, 2, 3, ...,数到 m 后,两个人同时出列。然后从出列的下一个人又从 1 开始继续进行双向报数,数到 m 的那两个人同时出列,...;。依此重复下去,直到圆桌周围的人全部出列。直到圆桌周围只剩一个人为止。

  如果双向报数报到 m 时落在同一个人身上,那本次出列的只有一个人。

  例如:5,1,2。则总共5个人,从1开始顺时针报数,数到2,定位编号2;同时从1开始报数数到2,定位编号5;2和5同时出列。然后继续开始报数,顺时针报数3,4,定位到4;逆时针报数4,3,定位3;4和3同时出列。最后剩余的为编号1。输出为:2-5,4-3,1,。

  如果输入:6,2,3。则输出:4-6,2,1-3,5,。其中第2次只输出一个2,表示第二次双向报数时,恰好都落在编号2上,所以只有一个编号出列。

输入:n,k,m

输出:按照出列的顺序依次输出编号。同时出列编号中间用减号"-”连接。

非法输入的对应输出如下

a)

输入:n、k、m任一个为0
输出:n,m,k must bigger than 0.

b)

输入:k>n
输出:k should not bigger than n.

解题思路

这里就需要用到双向循环链表(

struct p {int id;struct p* prior, * next;
};
struct p first, * ppre = &first, * pnex = (struct p*)malloc(sizeof(struct p));first.id = 1;first.next = pnex;first.prior = NULL;struct p* kth=NULL;//kth是第k个人(开始元素)if (k == 1) kth = &first;for (int i = 1; i < n - 1; i++){pnex->id = i + 1;if (i + 1 == k){kth = pnex;}pnex->prior = ppre;pnex->next = (struct p*)malloc(sizeof(struct p));ppre = pnex;pnex = pnex->next;}pnex->id = n;if (k == n) kth = pnex;pnex->prior = ppre;pnex->next = &first;first.prior = pnex;//循环

接下来还是一个过程模拟。注意双向之后就会多出几个问题:

(1)怎么判断出圈结束?也就是说有两种出口,要么最后是两人一起出圈,要么只剩一个了。另外由于有两个指针,怎么判断剩余几人?(我采用的是使用一个变量cnt)

(2)输出会不一样,要讨论几个人出圈的问题

(3)怎么去考虑出圈之后剩余元素的连接?

显然需要讨论,建议不要直接上手、先模拟一下,不然很容易出现RE。

我的做法是把两种方向的当前数字的指针命名为f(irst)cur(rent),s(econd)cur(rent),有三种情况:

对应的代码分别是:

​//同一个数的情况  *1
fcur->next->prior = fcur->prior;
fcur->prior->next = fcur->next;//delete//两个数的情况
//相邻  *2
if (fcur->next == scur){fcur->prior->next = scur->next;scur->next->prior = fcur->prior;//delete
}
else if (scur->next==fcur){scur->prior->next = fcur->next;fcur->next->prior = scur->prior;//delete
}
//不相邻  *3
else{fcur->prior->next = fcur->next;fcur->next->prior = fcur->prior;scur->prior->next = scur->next;scur->next->prior = scur->prior;//delete
}​

贴源代码

到这里就基本完整了,但诸如在“%d-%d”中始终是由小到大的这个方向的数在前的这些细节可能还需要打磨一下吧。

#include<iostream>
#include<cstdlib>
using namespace std;struct p {int id;struct p* prior, * 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, * ppre = &first, * pnex = (struct p*)malloc(sizeof(struct p));first.id = 1;first.next = pnex;first.prior = NULL;struct p* kth=NULL;if (k == 1){kth = &first;}for (int i = 1; i < n - 1; i++){pnex->id = i + 1;if (i + 1 == k){kth = pnex;}pnex->prior = ppre;pnex->next = (struct p*)malloc(sizeof(struct p));ppre = pnex;pnex = pnex->next;}pnex->id = n;if (k == n){kth = pnex;}pnex->prior = ppre;pnex->next = &first;first.prior = pnex;/*cur=&first;for(int w=0;w<n+1;w++){printf("%d\n",cur->id);cur=cur->next;}cur = &first;for (int w = 0; w < n + 1; w++){printf("%d\n", cur->id);cur = cur->prior;}还是为了检验链表的建立*/int cnt=n;//用于检测剩余几个数struct p *fcur, *scur;fcur = scur = kth;while (cnt!=2&&cnt!=1){for (int i = 1; i <= m - 1; i++){fcur = fcur->next;scur = scur->prior;}//分别步入if (fcur == scur){printf("%d,", fcur->id);cnt--;fcur->next->prior = fcur->prior;fcur->prior->next = fcur->next;//deletefcur = fcur->next;scur = scur->prior;//next}//*1else{printf("%d-%d,", fcur->id, scur->id);cnt -= 2;if (fcur->next == scur){fcur->prior->next = scur->next;scur->next->prior = fcur->prior;//deletefcur = scur->next;scur = fcur->prior;//next}else if (scur->next==fcur){scur->prior->next = fcur->next;fcur->next->prior = scur->prior;//deletefcur = fcur->next;scur = scur->prior;//next}//*2else{fcur->prior->next = fcur->next;fcur->next->prior = fcur->prior;scur->prior->next = scur->next;scur->next->prior = scur->prior;//deletefcur = fcur->next;scur = scur->prior;//next}//*3}}if (cnt == 2){printf("%d-%d,", fcur->id, scur->id);}else{printf("%d,", fcur->id);}printf("\n");return 0;
}​

继续学习

拓展:

懒惰删除:并没有真正的删除某个节点,而是以一种标记标记了该节点,使得该节点在以后的遍历过程中会被忽略。
这种删除方式最大的优点就是删除的常数时间十分小,执行时高效;编程时很容易实现,因为不需要涉及指针和内存free()等的操作;而且在经常需要重新加入相同元素的时候方便更改。
但是,当我们需要大量的删改一个链表的时候,也就是说十分动态的变化的表中,会造成非常严重的空间浪费,而且可能最后释放内存时还会造成可观的时间浪费。一些能够时刻将元素维持在较少个数的表则适合进行真正的删除。

本题可以使用懒惰删除。

这只需要将删除操作改为标记,然后在遍历时跳过即可;然而可能还是有一些细节问题需要注意。

在原有代码中,需要把步入、所有//next操作全部更改为:

fcur = fcur->next;
while(fcur->flag == true) fcur = fcur->next;
scur = scur->prior;
while(scur->flag == true) scur = scur->prior;//next

之所以使用while,是因为很有可能有连续的很多已删除结点。

然后再在原有代码中的所有//delete操作改为flag=true(要在结构体中加一个bool flag啦)。

fcur->flag = true;
scur->flag = true;//delete

——2022.9.25   /9.27添加

2.3.Josephus_problem_bidirectional 双向约瑟夫问题相关推荐

  1. 《恋上数据结构第1季》单向循环链表、双向循环链表以及约瑟夫环问题

    循环链表(CircleList) 链表的接口设计 单向循环链表 单向循环链表完整源码 双向循环链表 双向循环链表完整源码 双向循环链表解决约瑟夫环问题 如何发挥循环链表的最大威力? 静态链表 数据结构 ...

  2. C 约瑟夫双向生死游戏

    C语言 约瑟夫双向生死游戏 项目简介 约瑟夫双向生死游戏是在约瑟夫生者死者游戏的基础上,正向计数后反向计数,然后再正向计数.具体描述如下:30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分:因 ...

  3. 约瑟夫双向生死游戏c语言代码,约瑟夫生死游戏(含源代码可以运行)本科毕业设计.doc...

    约瑟夫生死游戏(含源代码可以运行)本科毕业设计 湖南商学院 数据结构与算法 课程设计 题 目约瑟夫双向生死游戏学生姓名梁子嫣学 号140920043学 院计算机工程与信息学院专业班级计科1402指导教 ...

  4. c语言实现约瑟夫双向生死游戏(附有详细代码)

    Reborn Terran Emperor 项目简介 约瑟夫双向生死游戏是在约瑟夫生者死者游戏的基础上,正向计数后反向计数,然后再正向计数... 具体描述如下:30个旅客同乘一条船,因为严重超载,加上 ...

  5. 约瑟夫双向生死杀人游戏, 逢3必杀, C语言杀人游戏

    实验内容: 约瑟夫生死游戏问题有如下几种表述: 表述一:古代某法官要判决N个犯人的死刑,他有一条荒唐的法律,将犯人站成一个圆圈,从第S个人开始数起,每数到第D个犯人,就拉出来处决,然后再数D个,数到的 ...

  6. 约瑟夫双向生死游戏c语言代码,数据结构程序设计——约瑟夫双向生死杀人游戏...

    首先是主函数: /** *版权所有(C)2017,ShangWenZhe * *文件名称:main.cpp *文件标识:无 *内容摘要:本文件的作用是调用算法库里面的函数,完成题目要求. *其它说明: ...

  7. 活用内核链表解决约瑟夫斯问题

    约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为"约瑟夫环",也有的地方叫做"丢手绢". 问题 ...

  8. step3 . day3 数据结构之线性表 单项循环链表和双向循环链表

    1. 使用单项循环链表解决约瑟夫问题: #include <stdio.h> #include <stdlib.h> typedef struct looplist{ int ...

  9. 顺序表、链表、双向循环链表

    顺序表.链表.双向循环链表 SeqList.h #pragma once #include<stdio.h>#define ListSize 100 //线性表的最大长度 typedef ...

最新文章

  1. JS计算两个时间相差多久,相差年,月,日,小时,分钟
  2. Linux系统基础调优
  3. 【安全漏洞】利用CodeQL分析并挖掘Log4j漏洞
  4. 给微软的依赖注入框架写一些扩展方法
  5. 我的代码第一次运行时的样子
  6. Ubuntu软件安装
  7. java中断异常_Java中断异常 InterruptedException 的正确处理方式
  8. HDU5695 Gym Class【拓扑排序】
  9. 软件开发中的完整测试所包括的环节UT、IT、ST、UAT
  10. 开源监控——cacti
  11. mysql存储emij表情_【MySQL】存储emoji表情报错(Incorrect string value: '\xF0\x9F\x98\x82\xF0\x9F...')的解决方案...
  12. 风景怡人一个生态村子 -国稻种芯-百色:华润谋定希望小镇
  13. 单片机实验(十二)单片机矩阵按键控制数码管
  14. Codeforces Round #572(div2)部分题解(A~C,E)
  15. 大数据技术学习带来的思考
  16. c++控制台工程,窗口最小化
  17. chrome硬件加速_如何在Chrome中打开和关闭硬件加速
  18. 在中文版Windows 10 中安装日语支持
  19. 网络安全学习--DNS部署与安全
  20. Linux鸟哥的私房菜读后感(菜鸟的读后感)

热门文章

  1. 【C++】1085:球弹跳高度的计算(信息学奥赛)
  2. 气候变化通过影响饮食塑造微生物决定人类进化?
  3. 安装程序找不到office.zh-cn/msvcr80.dll
  4. Wireshark下载及安装
  5. java 用面向接口编程的方式开发打印机_Java面向接口编程之简单工厂模式示例
  6. C语言学习笔记(一)
  7. COMSOL多孔材料 孔隙介质模型
  8. 计算机毕业设计Java高校体育器材及场地管理(源码+系统+mysql数据库+Lw文档)
  9. open淘宝酒店API open淘宝属性API open淘宝类目API
  10. 神经网络结构图绘图软件,大脑神经网络结构图片