约瑟夫环

约瑟夫环问题 ( 最简单的数学解法)

可紊 提交于 2020-03-01 14:27:54
基本问题描述: 已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。(也类似于变态杀人狂问题)通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。通常,我们会要求输出最后一位出列的人的序号。那么这里主要研究的是最后一个出列的人的序号要怎么确定。 当n,m数据量很小的时候,我们可以用循环链表模拟约瑟夫环的过程。当模拟到人数等于1的时候,输出剩下的人的序号即可。 具体解法 这种方法往往实现起来比较简单,而且也很容易理解。但是时间复杂度却是很糟糕的,达到了O(n m),这样的话,其实在n,m比较大的时候(n m达到10^8或者更大),那么要得出结果往往需要耗费很长的时间,但是我们可以运用一点数学上的技巧,将最后结果推导出来。 为了简化出列的过程: 首先我们把这n个人的序号编号从0~n-1(理由很简单,由于m是可能大于n的,而当m大于等于n时,那么第一个出列的人编号是m%n,而m%n是可能等于0的,这样编号的话能够简化后续出列的过程),当数到m-1的那个人出列,因此我们编号完成之后,开始分析出列的过程: 第一次出列: 一开始的时候,所有人的编号排成序列的模式即为: 0,1,2,3,4,5...n-2,n-1

约瑟夫环问题的程序

大憨熊 提交于 2020-03-01 14:27:40
#include <stdio.h> #include <stdlib.h> typedef struct lnode { int data; int code; struct lnode* next; }lnode, * list; /////////////////////////////////////////////////////////////////////////////// //辅助函数 lnode* CreateNode() { lnode* p = (lnode*) malloc (sizeof(lnode)); if (!p) { exit(0); } return p; } void DestoryNode(lnode* p) { free(p); } list ListInsertFront(list l) { lnode *p = CreateNode(); p->next = l; return p; } lnode* ListRemoveNext(list prev) { lnode* p = prev->next; prev->next = p->next; return p; } /////////////////////////////////////////////////////////////////////////////// //

约瑟夫环问题之猴子选大王

≯℡__Kan透↙ 提交于 2020-03-01 14:26:22
猴子选大王的描述为:n只猴子围成一圈,顺时针方向从1到n编号。之后从1号开始沿顺时针方向让猴子从1,2,。。。,m依次报数,凡是报到m的猴子,都让其出圈,取消候选资格。然后不停的按顺时针方向报数,让报出m者出圈,最后剩下一个就是猴王。 #include <stdio.h> #include <stdlib.h> struct mon { int num; struct Node *next; }; struct mon *create(int n){// 新建链表,无头结点,并且最后一个结点的next指向第一个结点 int i; struct mon *p,*tail,*head; p=(struct mon *)malloc(sizeof(struct mon)); p->num=1; p->next=NULL; head= p; tail=p; for(i=2;i<=n;i++){ p=( struct mon *)malloc(sizeof(struct mon)); p->num=i; tail->next=p; tail=p; p->next=NULL; } tail->next=head; return head; } int sel( struct mon *head,int m,int n){ int num1=0;//表示猴子报数的计数变量 int count

使用循环链表实现约瑟夫环(围圈报数问题)

巧了我就是萌 提交于 2020-03-01 14:26:11
刚开始学C,碰到经典的围圈报数问题,现先将实现代码附下: #include<stdio.h> #include<stdlib.h> struct LNODE{            //链表定义 int data; struct LNODE *next; }; typedef struct LNODE Lnode; typedef struct LNODE *LinkList; struct LNODE *create(int s[])      //创建单项循环链表 { struct LNODE *head=NULL,*p=NULL,*last=NULL; int i=0; head=(struct LNODE *)malloc(sizeof(struct LNODE)); if(!head) printf("memory allocation error!"); if(s[0]!=0) { head->data=s[0]; head->next=head; last=head; i++; while(s[i]!=0)      //判断是否为0,为0则结束 { p=(struct LNODE*)malloc(sizeof(struct LNODE)); last->next=p; p->data=s[i];   p->next=head; last=p; i++; } }

约瑟夫环

删除回忆录丶 提交于 2020-03-01 14:24:49
问题概述 编号为1至n的n个人围成一圈(之后编号为为i的人简称i)i在i+1左边,n在1的左边。 从1开始报数,i报完数后在他右边的人继续报数,如果i所报的数为m的倍数那么这个人就出圈 持续报数到只剩一个人 按出圈顺序输出编号。 方法1 我们设sta[i]为0表示i已经出圈,为1表示还在圈内 设pl为当前所处位置 如果一个人出圈就把sta修改为0 然后模拟报数过程并且跳过sta[i]=0的位置即可 #include<cstdio> const int N=1e5+5; int sta[N]; int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) sta[i]=1; for(int i=1,pl=1;i<=n;++i) for(int j=m;j;){ if(sta[pl]){ --j; if(!j){ printf("%d ",pl); sta[pl]=0; } } pl++; if(pl>n) pl-=n; } return 0; }  方法2: 与之前的方法相似,只是用链表连接 删除时将其从链表中清除即可。 #include<cstdio> const int N=1e5+5; int nex[N],pre[N]; int main(){ int n,m; scanf("%d%d",&n,&m); for

2018icpc沈阳-K.Let the Flames Begin (约瑟夫环问题)

会有一股神秘感。 提交于 2020-03-01 14:24:19
题意: 转化为经典约瑟夫环问题: N个人围成一圈,从第一个开始报数,第K个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。 然后现在给出 N(标号1~N)个人,每隔K个将要被杀掉,问第 M 个被杀掉的 标号是多少。(与顺时针或者逆时针无关)。 思路: 基础递推原理可以参考下 博客: https://www.cnblogs.com/jjscm/p/4463555.html 2019.11.28:发现之前自己当时写的有很多问题没有弄清楚 首先约瑟夫环的递推公式(可以模拟一下递归,先到只剩下最后一个人,然后每一次往前一个状态回递推,求得这个人所在位置–注意这个递推公式求得是当前状态序列的位置下标 即约瑟夫环通过递推,从最后的状态得到最初的状态。而由于要问第M个被杀掉的是谁,所以我们的初始状态不是从只剩下最后一人开始(长度为1),而是从(n-m+1)状态(还剩下n-m+1个人)开始 而根据约瑟夫环递推式:(表示N个人,第M个出队的人位置) f[n][m] = (f[n-1][m-1] + k)%n; 所以转换后为 f[n-m+i] = (f[n-m+i-1] + k) % (n - m + i); 由于题中数据大小为:1 ≤ n, m, k ≤ 10 18 ,以及 min{m,k} 总和不超过 2 × 10 6 ,m >= n.

用循环链表解决约瑟夫环的问题

北城以北 提交于 2020-03-01 14:24:08
约瑟夫环问题简介 约瑟夫环问题的原来描述为,设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。 稍微简化一下。 问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。 解题思路 将每个人的编号作为结点值,因为报数是循环着来的,故可以利用循环链表。 因为最后只有一个人获胜,所以筛选的次数为n-1次,里面的每次循环m次,表示循环到的第m个人淘汰。 代码及运行截图 #include<iostream> #include<cstdio> using namespace std; struct node { int value; struct node *next; }; int n, m;//共n个人,报数报到m的人淘汰 //创建循环链表 struct node *init() { struct node *h = NULL; struct node *t = NULL; struct node *p = NULL; for (int i = 1; i <= n; ++i) { p = (struct node*)malloc

循环单链表,解决约瑟夫问题

℡╲_俬逩灬. 提交于 2020-03-01 14:23:47
约瑟夫问题: 编号为1~N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),开始任选一个正整数作为报数上限值M,从第1个人按顺时针方向自1开始顺序报数,报到M时停止报数。报M的人出列,将他的密码作为新的M值,从他顺时针方向上的下一个人开始从1报数,如此下去,直至所有人全部出列为止。 解析: 显然当有人退出圆圈后,报数的工作要从下一个人开始继续,而剩下的人仍然围成一个圆圈,因此可以使用循环单链表。退出圆圈的工作对应着表中节点的删除操作,对于这种删除操作频繁的情况,选用效率较高的链表结构,为了程序指针每次都指向一个具体的代表一个人的节点而不需要判断,链表不带头节点。因此,对于所有人围成的圆圈所对应的数据结构采用一个不带头节点的循环链表来描述。 1 #include "stdafx.h" 2 #include <iostream> 3 #include "string.h" 4 using namespace std; 5 6 typedef struct node 7 { 8 int data; 9 node *next; 10 }node; 11 12 node *create(int n) //创建节点数量为n的单向循环链表 13 { 14 node *pRet = NULL; 15 if(n != 0) 16 { 17 int n_idx = 1; 18 node *p

常用算法(C#): 约瑟夫环问题

99封情书 提交于 2020-03-01 14:23:14
约瑟夫环问题 : 设有n个人围坐在圆桌周围,现从某个位置m(1≤m≤n)上的人开始报数,报数到k的人就站出来。 继续下一个人,即原来的第k+1个位置上的人,又从1开始报数,再报数到k的人站出来。依此重复下去,直到全部的人都站出来为止 using System; using System.Collections.Generic; using System.Text; namespace ExJose { class ClassJose { //从第start人开始计数,以alter为单位循环记数出列,总人数为total public int[] Jose(int total, int start,int alter) { int j, k = 0; //count数组存储按出列顺序的数据,以当结果返回 int[] count = new int[total + 1]; //s数组存储初始数据 int[] s = new int[total + 1]; //对数组s赋初值,第一个人序号为0,第二人为1,依此下去 for (int i = 0; i < total; i++) { s[i] = i; } //按出列次序依次存于数组count中 for (int i = total; i >= 2; i--) { start = (start + alter - 1) % i; if

2-6约瑟夫环

痞子三分冷 提交于 2020-02-29 17:05:30
题目描述 解题方法1 如果链表数据为 10 20 30 40 50 60 70 80 90 。那么按照如上规则,节点的删除顺序应该为 30 60 90 40 80 50 20 70 最后剩下的节点为10。 最常规的方法,我们只需要设置一个计数器count,然后每遍历一个节点count加一,当count==m时删除当前节点并让count=0,不断遍历节点和删除直到剩下最后一个节点退出循环。 需要注意的是这里使用不带头节点的链表,避免头节点对问题的干扰。 每次要遍历m次才能找到一个要删除的节点,链表一共有n个节点,整个过程的时间复杂度为 m*n。 public class Test { public static void main ( String [ ] args ) throws Exception { int [ ] arr = { 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 } ; Node head = create ( arr ) ; yuesefu ( head , 2 ) ; } public static Node yuesefu ( Node head , int m ) { if ( head == null || head . next == head || m < 0 ) { return head ; } Node