约瑟夫环

约瑟夫环 JosephusProblem

房东的猫 提交于 2020-01-08 23:47:12
问题描述: n个人 ( 编号0~(n-1) ),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。 最直白的方法就是用链表去模拟整个过程就好了,但是这个的复杂度有点高,算不上一个非常优秀的做法。 下面进行推导,看是否能够推出一个通用的公式这样就可以直接得出答案: 初始情况: 0, 1, 2 ......n-2, n-1 (共n个人) 第一个人(编号一定是(m-1)%n,设之为(k-1) ,读者可以分m<n和m>=n的情况分别试下,就可以得出结论) 出列之后, 剩下的n-1个人组成了一个新的约瑟夫环(以编号为k==m%n的人开始): k k+1 k+2 ... n-2, n-1, 0, 1, 2, ...,k-3, k-2 现在我们把他们的编号做一下转换: k --> 0 k+1 --> 1 k+2 --> 2 ... ... k-2 --> n-2 k-1 --> n-1 变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗! x ->x' (这正是从n-1时的结果反过来推n个人时的编号!) 0 -> k 1 -> k+1 2 -> k+2 ... ... n-2 -> k-2 变回去的公式  x'=(x+k)%n 递推公式 f [1] = 0; f

约瑟夫问题

孤街醉人 提交于 2020-01-05 03:57:37
约瑟夫问题的来历 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。 问题分析与算法设计 约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。 题目中30个人围成一圈,因而启发我们用一个循环链表来表示。可以使用结构数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记,为1表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为0

HDU 3089 (快速约瑟夫环)

北战南征 提交于 2020-01-01 20:58:43
题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=3089 题目大意 :一共n人。从1号开始,每k个人T掉。问最后的人。n超大。 解题思路 : 除去超大的n之外。就是个约瑟夫环的裸题。 约瑟夫环递推公式,n为人数,k为步长。 f(1)=0 f(n)=[f(n-1)+k]%i i∈[2,n] f(n)还要经过起始位置修正,设起始位置为s,即ans=[f(n)+s]%n。 基本约瑟夫环优化就是当k=1的时候,每次递推就是在+1,可以直接算出来快速跳过,f(n)=f(1)+n-1 当n超大的时候,可以照着这种思路快速简化递推过程。在递推后期,f(x)+k在很长的周期内<i,假设有m个周期, 那么这些周期合并后的结果相当于f(x)+m*k。可以快速跳过。条件限制是: f(x)+m*k<i+(m-1) 可以推出来: 当m=1时,条件限制: f(x)+k<i 当m=2是,条件限制: f(x+1)+k<i+1=f(x)+2*k<i+1 当m=m时,条件限制:f(x)+m*k<i+(m-1) 化简有m<(i-f(x)-1)/(k-1),若能整除,就是(i-f(x)-1)/(k-1)-1,否则就是(i-f(x)-1)/(k-1)直接取整。 这样,i+=m,f(x)+=m*k,快速跳过了中间过程。 若i+m>n,说明快速跳越界了,这时候可以直接算出f(n

约瑟夫环公式法解释

本小妞迷上赌 提交于 2019-12-29 18:57:11
公式法: F(N,M) = (F(N-1,M) + M) % N F(1,M) = 0 (索引从0开始) F是每一轮中存活的人对应的编号。 递推过程如下: 已知F(1,M) = 0,即只有一个人的队伍中,存活的肯定是编号0 从最后一轮F(1,M)求解倒数第二轮F(2,M): 倒数第二轮有两个人,杀掉一个人后剩余一人; 那对应的报号顺序为: 0 1 2 0 ,(最后一个0对应的存活的人,也就是最后一轮的编号) 实际报号顺序对应的编号是:0 1/ 0 1 (上下对应,最后剩的人对应编号为1.) 故倒数第二轮(0,1),剩余的是编号为1 倒数第三轮同理 报号顺序: 0 1 2 0 1 (最后的0,1表示这轮之后存活的人,也就是倒数第二轮中人的编号) 实际编号: 0 1 2/ 0 1 (这一轮实际有三个人,对应实际编号,最后剩余的人也是编号1) 故倒数第三轮(0,1,2),剩余的是编号为1 倒数第四轮 报号顺序: 0 1 2 0 1 2(最后的0,1,2表示这轮之后存活的人,也就是倒数第三轮轮中人的编号) 实际编号: 0 1 2 3/ 0 1(这一轮实际有4个人,对应实际编号,最后剩余的人也是编号1) 故倒数第三轮(0,1,2,3),剩余的是编号为0 倒数第五轮 报号顺序:0 1 2 0 1 2 实际顺序:0 1 2 3 4/ 0 故下一轮中这一轮中对应的是编号3 归纳可以得到结论

约瑟夫问题升级版

纵然是瞬间 提交于 2019-12-27 01:48:24
约瑟夫问题升级版 编号为1~N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数,可以自由输入),开始人选一个正整数作为报数上限值M,从第一个人按顺时针方向自1开始顺序报数,报道M时停止报数。报M的人出列,将他的密码作为新的M值,从他顺时针方向上的下一个人开始从1报数,如此下去,直至所有人全部出列为止。 分析: 升级版的约瑟夫问题,就是在每个人的手中拿了一个密码,当这个人死了之后,拿着这个手中的密码,进行下一次剔除。 如上图所示,p 第一个拿到的是 3 ,则从头开始第 3 个人剔除,依次这样进行下去,直到所有人被剔除。 代码实现: class JosephusLoopUpper { private Node head ; //头指针 private Node rear ; //尾指针 private int size ; private int M ; //每个人手中所拿的密码数字 public JosephusLoopUpper ( ArrayList < Integer > list , int M ) { //创建一个给定人个数的环 head = new Node ( list . get ( 0 ) , 0 , null ) ; //创建第一个点 并将尾指向头形成一个环 rear = head ; rear . next = head ; for ( int i = 1

约瑟夫问题代码实现

此生再无相见时 提交于 2019-12-26 07:58:47
问题:设编号1,2,·····n的n个人围成一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的人继续出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。 主函数测试代码: public static void main(String[] args){ Scanner scanner=new Scanner(System.in); //创建要给链表 CirSingleLinkedList cirSingleLinkedList=new CirSingleLinkedList(); //1.添加数据 cirSingleLinkedList.addBoy(5); //2.显示数据 System.out.println("环形单链表内的数据为:"); cirSingleLinkedList.list(); //3.出圈顺序(2->4->1->5->3) cirSingleLinkedList.countBoy(1,2,5); } 单链表类: class CirSingleLinkedList{ //先初始化一个头节点,头节点不能动,不存放具体的数据 private Boy first=new Boy(-1); public void countBoy(int startNo,int countNum,int nums){

循环链表的运用——约瑟夫环

拜拜、爱过 提交于 2019-12-26 04:52:55
今天写循环链表里的约瑟夫环(严蔚敏版数据结构题集) 问题描述: 约瑟夫问题的一种描述是:编号为1,2,3,4……,n的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数的上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1开始报数,如此下去,直至所有人的全部出列为止。试设计一个程序求出出列顺序。 基本要求 利用简单的单向循环列表存储结构模拟此过程,按照出列的顺序印出各人的编号。 测试数据 m的初值为20;n=7,7个人的密码分别为:3,1,7,2,4,8,4,首先m的值为6(正确的出列顺序为6,1,4,7,2,3,5)。 代码如下: #include<iostream> using namespace std; typedef struct Lnode {//定义结构体 int data; struct Lnode *next; }*Linklist; void joseph(int n,int k,int *m) { Linklist p,r,list=NULL; int i; for(i=0;i<n;i++) { p=(Linklist)malloc(sizeof(Lnode)); p->data=i+1; if(list==NULL) { r

古老而简单的约瑟夫问题

天大地大妈咪最大 提交于 2019-12-26 04:45:48
约瑟夫问题的传说: 著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 抽象出来问题分析与算法设计: 已知n个人(以编号1、2、3~~n分别表示)围坐在一张圆桌周围,从编号为k的人开始报数,数到m的那个人出列,他的下一个人又从1开始报数,数到m的那个人出列,依次规律重复下去,直到圆桌周围的人全部出列。   例如:n = 9, k = 1, m = 5   【解答】   出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。 程序代码: 很明显这个问题用循环连接即可解决。 1 #include <iostream> 2 using namespace std; 3 4 //定义节点结构体 5 struct node{ 6 int data; 7 struct node *link; 8 }; 9 10 void Josephus(int n, int k, int

约瑟夫环问题

◇◆丶佛笑我妖孽 提交于 2019-12-26 03:54:28
约瑟夫环问题,是经典的算法题。百度百科解释如下 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依次规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。 解决方案很多种,不过我都看不太懂(原谅我实在太愚笨了) 这里我先举个例子 假如 有5个人 数到3的要出列,那么过程是这样的 开始是 1 2 3 4 5 毫无疑问 从1开始数,3号肯定要离开 得到 1 2 4 5 然后该4开始数了,那么数到5 再轮回头数 1要离开 得到 2 4 5 然后该2开始数,数到5 5离开 得到 2 4 然后发现5离开了后面轮回来还是2 那就还是2开始数 数回自己 自己离开 得到 4 整个过程就是 3离开 1离开 5离开 2离开 4留了下来 如果转换为代码怎么实现呢? 代码如下: <?php function joseph_problem($total=1,$number=1) { $array=range(1, $total);//把这些人都存入数组 $start=0;//开始数数的第一个人的位置 while (count($array)>1) { //说明里面还有人 $need_del = (

约瑟夫环问题的数组实现

限于喜欢 提交于 2019-12-26 03:51:45
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。 例如:n = 9, k = 1, m = 5 【解答】 出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。 以上摘录自 百度百科 /// <summary> /// 约瑟夫环 /// </summary> /// <param name="n">总数</param> /// <param name="m">开始位置</param> /// <param name="k">符合条件的数</param> /// <returns></returns> public void Josehp(int n, int m, int k) { //参数判断 是否异常 int[] array = new int[n]; for (int i = 0; i < n; i++) { array[i] = i + 1; } int count = 0; int number = n; while (number > 1) { for (int i = 0; i < n; i++) { if (m != 1) { i = m - 1; m = 1; } if (array[i] == 0