约瑟夫问题

合集下载

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

算法设计
Josephus jp=new Josephus(); int a[]=new int[n]; for(int i=0;i<n;i++){ a[i]=i+1; } jp.SortArray(a,n,m,k,g); } public void show(int[]b,int g){ for(int i=b.length-g;i<b.length;i++){ System.out.print(b[i]+" "); } }
• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 48 6 15 47 21 46 105 73 4 87 32 21 300 80 12 70 296 198 总人数n 起始号码k 循环数m 68 34 25
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。

约瑟夫问题

约瑟夫问题

约瑟夫问题
1
问题描述
n个人(n<=100)围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,……依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

输入格式:
n和m两个整数,中间用空格隔开。

m,n<=100。

输出格式:
出圈的编号。

输入样例:
10 3
输出样例:
3 6 9 2 7 1 8 5 10 4
2
问题分析
这是典型的约瑟夫问题,我们这里用模拟的方法。

利用循环一个一个数,定义一个计数变量count,表示记录从1开始数的人数;定义一个下标索引变量i,表示数的人所在的位置下标。

另外还要定义个记录出去人数的变量t。

循环的条件是t<=n。

我们用数组对应的值表示该下标的人是否出去了。

0表示存在,1表示出去。

如果数到最后一个记得还要从第一个开始。

3
参考代码如下。

约瑟夫问题及变种

约瑟夫问题及变种

“约瑟夫”问题及若干变种例1、约瑟夫问题(Josephus)[问题描述]M只猴子要选大王,选举办法如下:所有猴子按1…M编号围坐一圈,从第1号开始按顺序1,2,…,N 报数,凡报到N的猴子退出到圈外,再从下一个猴子开始继续1~ N报数,如此循环,直到圈内只剩下一只猴子时,这只猴子就是大王。

M和N由键盘输入,1≤N,M≤10000,打印出最后剩下的那只猴子的编号。

例如,输入8 3,输出:7。

[问题分析1]这个例题是由古罗马著名史学家Josephus提出的问题演变而来的,所以通常称为Josephus(约瑟夫)问题。

在确定程序设计方法之前首先来考虑如何组织数据,由于要记录m只猴子的状态,可利用含m个元素的数组monkey来实现。

利用元素下标代表猴子的编号,元素的值表示猴子的状态,用monkey[k]=1表示第k只猴子仍在圈中,monkey[k]=0则表示第k只猴子已经出圈。

程序采用模拟选举过程的方法,设变量count表示计数器,开始报数前将count置为0,设变量current 表示当前报数的猴子编号,初始时也置为0,设变量out记录出圈猴子数,初始时也置为0。

每次报数都把monkey[current]的值加到count上,这样做的好处是直接避开了已出圈的猴子(因为它们对应的monkey[current]值为0),当count=n时,就对当前报数的猴子作出圈处理,即:monkey[current]:=0,count:=0,out:=out+1。

然后继续往下报数,直到圈中只剩一只猴子为止(即out=m-1)。

参考程序如下:program josephus1a {模拟法,用数组下标表示猴子的编号}const maxm=10000;var m,n,count,current,out,i:integer;monkey:array [1..maxm] of integer;beginwrite('Input m,n:');readln(m,n);for i:=1 to m do monkey[i]:=1;out:=0; count:=0; current:=0;while out<m-1 dobeginwhile count<n dobeginif current<m then current:=current+1 else current:=1;count:=count+monkey[current];end;monkey[current]:=0; out:=out+1; count:=0end;for i:=1 to m doif monkey[i]=1 then writeln('The monkey king is no.',i);readlnend.[运行结果]下划线表示输入Input m,n:8 3The monkey king is no.7 {时间:0秒}Input m,n:10000 1987The monkey king is no.8544 {时间:3秒}[反思]时间复杂度很大O(M*N),对于极限数据会超时。

约瑟夫问题pascal

约瑟夫问题pascal

约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。

在计算机编程的算法中,类似问题又称为约瑟夫环。

又称“丢手绢问题”)据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从。

首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。

接着,再越过k-1个人,并杀掉第k个人。

这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

//详情请见百度百科。

[题目描述]Josephus问题是建立在历史学家Josephus的一个报告的基础之上,该报告讲述了他和40个士兵在公元67年被罗马军队包围期间签定的一个自杀协定,Josephus建议每个人杀死他的邻居,他很聪明的使自己成为这些人中的最后一个,因此获得生还。

21世纪的今天,我们将Josephus问题稍作扩展:设N个人围成一圈,依次从1到N编号,从第S号开始报数,报到K的人出列,求第T个出列的人的编号。

显然,Josephus面对的是N = 40, K = 2, S= 1, T = 40的退化情况。

[数据范围]30%的数据,K = 2100%的数据,1 <= N <= 1000000, 1<= K <= 2147483647, 1 <= S <= N, 1 <= T <= N最简单的推导方法应该是模拟。

实验一、约瑟夫问题

实验一、约瑟夫问题

实验一:约瑟夫问题求解一、问题描述1、实验题目:约瑟夫(Josephus)问题的一种描述是:编号为1,2,……,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

一开始任选一个正整数作为报数上线值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。

报m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。

2、基本要求:试设计一个程序,按出列顺序印出个人编号。

3、测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4。

m的初值为6,正确的出列顺序应为:6,1,4,7,2,3,5。

二、需求分析1、本程序用来求出含有密码的约瑟夫问题,可以输出所有人的出列顺序。

2 、程序运行后显示提示信息,提示用户输入一圈的人数n,接着输入每个人的密码,最后提示输入初始密码。

3、用户输入完毕后,程序自动输出运算结果。

三、概要设计1、设计思路n个人围成一圈,每个人的手中都有一个密码,这个密码决定了下一次报数的上限。

游戏规则:①给定一个初始密码②循环报数,报到密码值的人要出列,依次类推,直到所有的人都出列本程序要求输入的内容:n个人的密码及初始密码;本程序要求输出的内容:n个人出列的顺序。

2、数据结构为了实现上述功能,可以采用链式存储结构。

采用链式存储结构,定义了一个存储个人信息的结构体,及两个自定义函数,分别用于创建链表和约瑟夫出列操作。

①链表抽象数据类型的定义: #define SLNODE struct slnodeADT SLNODE{数据对象:D={ i a |i a ∈SLNODE, i=1,2,3.... }数据关系:R=φ}ADT SLNODE;②自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列{ 创建链表,为N 个人分配密码 }void Josef(SLNODE *p,int n)//进行约瑟夫操作{输入初始密码m;for(){ 将出列的结点删除,并输出出列序号;}}③本程序的保护模块:结构体模块主程序模块自定义函数模块调用关系:3、程序设计主要算法的流程图:create_SLnode( )算法流程图Josef( )算法流程图四、详细设计1、元素类型、结点的类型及指针#define SLNODE struct slnodeSLNODE//每个结点的结构体{int num;//num代表序号int code;//code代表密码SLNODE *next;};2、自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列,并将其尾指针指向第一个序号{SLNODE *r,*s;s=p;int i,m;cout<<"请给这"<<n<<"个人分配密码:"<<endl;for(i=0;i<n;i++){cout<<"请给第"<<i+1<<"个人输入密码:"<<endl;cin>>m;r=(SLNODE *)malloc(sizeof(SLNODE));r->code=m;r->num=i+1;r->next=s->next;s->next=r;s=s->next;}p=p->next;s->next=p;}void Josef(SLNODE *p,int n)//进行约瑟夫操作{p=p->next;int m;int i,j;SLNODE *r;cout<<"请输入初始密码:"<<endl;cin>>m;cout<<"依次出列的序号为:"<<endl;for(i=0;i<n-1;i++)p=p->next;for(i=0;i<n-2;i++){for(j=0;j<m-1;j++)p=p->next;cout<<(p->next)->num<<endl;m=(p->next)->code;r=p->next;p->next=r->next;}if(m%2==0)cout<<p->num<<endl<<(p->next)->num<<endl;elsecout<<(p->next)->num<<endl<<p->num<<endl;}3、主函数:int main(){SLNODE *p;int n;cout<<"请输入一圈的人数:"<<endl;cin>>n;p=(SLNODE *)malloc(sizeof(SLNODE));p->next=NULL;create_SLnode(p,n);Josef(p,n);return 0;}4、函数的调用关系:主函数main()调用自定义函数void create_SLnode(SLNODE *p,int n);/*创建队列*/与void Josef(SLNODE *p,int n);/*进行约瑟夫操作*/。

约瑟夫问题

约瑟夫问题

4、狐狸捉兔子
题目:
围绕着山顶有10个洞,狐狸要吃兔子,兔子说:“可以,但必须找到我,我就藏身于这十个洞中,你从10号洞出发,先到1号洞找,第二次隔1个洞找,第三次隔2个洞找,以后如此类推,次数不限。”但狐狸从早到晚进进出出了1000次,仍没有找到兔子。问兔子究竟藏在哪个洞里?
参考程序下载
--------------------------------------------------------------------------------
5、进制转换
题目:
将一个十进制自然数转换成二进制数,一般采取除2取余法。从键盘输入一个十进制自然数(约定该数小于等于Maxlongint),输出相应的二进制数
设有一天平,可用来称物体的质量,同时给出一个正整数n(n<=10)。
问题1:试设计n个砝码的质量,用它们能称出尽可能多的1,2,3,...连续整数质量,约定砝码可以放在天平的左右两个托盘中的任何一个或两个中。例如n=2,此时设计2个砝码的质量分别为1,3,则能同时称取1,2,3,4。
问题2:在给出n个砝码能称出最大质量范围内的一个质量x,试给出称取x的方案。如上例中:
6/7,7/8,1/1
编程求出n级法雷序列,每行输出10个分数。n的值从键盘输入。
--------------------------------------------------------------------------------
13、砝码设计
题目:
①把组成这个四位数的4个数字由小到大排列,形成由这四个数字组成的最大的四位数;
②把组成这个四位数的4个数字由大到小排列,形成由这四个数字组成的最小的四位数(如果含有数字0,则不足四位);

约瑟夫问题大全

约瑟夫问题大全

“约瑟夫”问题及若干变种林厚从例1、约瑟夫问题(Josephus)[问题描述]M只猴子要选大王,选举办法如下:所有猴子按1…M编号围坐一圈,从第1号开始按顺序1,2,…,N报数,凡报到N的猴子退出到圈外,再从下一个猴子开始继续1~ N报数,如此循环,直到圈内只剩下一只猴子时,这只猴子就是大王。

M和N由键盘输入,1≤N,M≤10000,打印出最后剩下的那只猴子的编号。

例如,输入8 3,输出:7。

[问题分析1]这个例题是由古罗马著名史学家Josephus提出的问题演变而来的,所以通常称为Josephus(约瑟夫)问题。

在确定程序设计方法之前首先来考虑如何组织数据,由于要记录m只猴子的状态,可利用含m 个元素的数组monkey来实现。

利用元素下标代表猴子的编号,元素的值表示猴子的状态,用monkey[k]=1表示第k只猴子仍在圈中,monkey[k]=0则表示第k只猴子已经出圈。

程序采用模拟选举过程的方法,设变量count表示计数器,开始报数前将count置为0,设变量current表示当前报数的猴子编号,初始时也置为0,设变量out记录出圈猴子数,初始时也置为0。

每次报数都把monkey[current]的值加到count上,这样做的好处是直接避开了已出圈的猴子(因为它们对应的monkey[current]值为0),当count=n时,就对当前报数的猴子作出圈处理,即:monkey[current]:=0,count:=0,out:=out+1。

然后继续往下报数,直到圈中只剩一只猴子为止(即out=m-1)。

参考程序如下:program josephus1a {模拟法,用数组下标表示猴子的编号}const maxm=10000;var m,n,count,current,out,i:integer;monkey:array [1..maxm] of integer;beginwrite('Input m,n:');readln(m,n);for i:=1 to m do monkey[i]:=1;out:=0; count:=0; current:=0;while out<m-1 dobeginwhile count<n dobeginif current<m then current:=current+1 else current:=1;count:=count+monkey[current];end;monkey[current]:=0; out:=out+1; count:=0end;for i:=1 to m doif monkey[i]=1 then writeln('The monkey king is no.',i);readlnend.[运行结果]下划线表示输入Input m,n:8 3The monkey king is no.7 {时间:0秒}Input m,n:10000 1987The monkey king is no.8544 {时间:3秒}[反思]时间复杂度很大O(M*N),对于极限数据会超时。

约瑟夫问题实验报告

约瑟夫问题实验报告

一、实验目的1. 理解并掌握约瑟夫问题的基本原理和解决方法。

2. 学习使用循环链表解决线性问题。

3. 提高编程能力和算法设计能力。

二、实验原理约瑟夫问题(Josephus Problem)是一个著名的数学问题,也称为约瑟夫环问题。

问题描述为:N个人围成一圈,从第一个人开始按顺时针方向报数,每数到M的人出列,然后从下一个人开始继续报数,直到所有人都出列。

我们需要找到一种方法,计算出每个人出列的顺序。

三、实验内容1. 创建一个循环链表,模拟N个人围成一圈。

2. 编写一个函数,实现报数和出列操作。

3. 输出每个人出列的顺序。

四、实验步骤1. 定义一个循环链表节点结构体,包含编号和指向下一个节点的指针。

2. 创建一个循环链表,包含N个节点,节点的编号依次为1到N。

3. 编写一个函数`kill(int m, int n)`,实现报数和出列操作:- 初始化一个指针指向第一个节点。

- 从第一个节点开始,按照报数上限M进行报数,每数到M的人出列。

- 更新指针,指向下一个节点,继续报数。

- 重复上述步骤,直到所有节点都被删除。

4. 输出每个人出列的顺序。

五、实验代码```c#include <stdio.h>#include <stdlib.h>// 定义循环链表节点结构体typedef struct Node {int number; // 节点编号struct Node next; // 指向下一个节点的指针} Node;// 创建循环链表Node create(int n) {Node head = NULL, tail = NULL, temp = NULL; for (int i = 1; i <= n; i++) {temp = (Node)malloc(sizeof(Node));temp->number = i;temp->next = NULL;if (head == NULL) {head = temp;tail = temp;} else {tail->next = temp;tail = temp;}}tail->next = head; // 形成循环链表return head;}// 输出循环链表void printList(Node head) {Node temp = head;do {printf("%d ", temp->number);temp = temp->next;} while (temp != head);printf("\n");}// 解决约瑟夫问题void josephus(int m, int n) {Node head = create(n);Node temp = head, pre = NULL;for (int i = 1; i <= n; i++) {for (int j = 1; j < m; j++) {pre = temp;temp = temp->next;}printf("%d ", temp->number);pre->next = temp->next; // 删除节点 free(temp);temp = pre->next;}printf("\n");}int main() {int m, n;printf("请输入报数上限M: ");scanf("%d", &m);printf("请输入人数N: ");scanf("%d", &n);printf("初始站队为: ");josephus(m, n);return 0;}```六、实验结果与分析通过运行实验代码,可以得到每个人出列的顺序。

约瑟夫问题

约瑟夫问题




第一个人出列:从 数器 j 从 1 数到 person[i] <- 0 第二个人出列:从 数器 j 从 1 数到 person[i] <- 0 „„ n-1个出列:„„
k = 0 的后继开始扫描数组,报数计 m,该人出列,把他的状态置成 0,
k = i 的后继开始扫描数组,报数计 m,该人出列,把他的状态置成 0,
此题如果意思是定位第 m 个元素,当不需要跳过任何元素 时。 从 k 开始报数第 m 个元素是: ((( k + m - 1 ) – 1 ) mod ) n +1
“遍历”和“数到”的区别: 遍历到的每个元素需要做相同的操作,而“数到m”第m个 元素和前 m-1 个元素有不同的操作。
如果需要跳过某些标记的元素,就无法直接确定第 m 个元 素的位置,则需要按顺序逐个扫描元素,跳过不符合要求的 元素,对符合要求的元素进行计数。 此处注意第一个数也许需要跳过,第一个报数的编号也许不 是 k,不能直接从 1 计数。 i <- k j <- 0 while ( j< m ) do begin i <- (i mod n) + 1 if ( person[i] <> 0 ) then j <- j+1 end

2.1
模拟方法
2.2
2.1.1 数组+标记 2.1.2 循环链表+删除节点
数学模型



定义数组: array person[1..n]代表 n 个人的状态; 第i个元素代表编号为i的人的状态,下标代表人的编号; 状态: 1代表没有出列,0代表已出列。 使用线性数组表示环状圆圈 * 数组元素间相邻关系和环状结构元素间相邻关系有所不 同,person[n]没有后继节点,person[1]没有前驱节点。 数组可以使用下面方法实现环状结构的相邻关系: person[i]后继节点的下标为j: if i = n then i<- 1 else i <- i+1 或者 i <- (i mod n) +1

约瑟夫问题

约瑟夫问题

约瑟夫问题简介约瑟夫问题是一个经典的数学问题,由弗拉维奥·约瑟夫斯(Josephus Flavius)所提出。

问题的背景是:在一个固定长度的圆桌周围围坐着n个人,从第一个人开始报数,报到m的人出圈,然后从下一个人开始重新报数,直到剩下最后一个人。

问题是,给定n和m,求最后剩下的人的编号。

解法暴力破解法最直观的解法是使用循环和数组来模拟这个过程。

首先,我们用一个数组来表示这些人的编号,例如[1, 2, 3, ..., n]。

然后我们在循环中模拟报数的过程,每次报到m的人就从数组中删除。

当数组中只剩下一个元素时,就找到了最后剩下的人。

def josephus(n, m):people = list(range(1, n +1))idx =0while len(people) >1:idx = (idx + m -1) % len(people)people.pop(idx)return people[0]这种解法的时间复杂度为O(n*m),并且在n很大的情况下,性能会变得很差。

数学公式法约瑟夫问题其实存在一个更巧妙的解法,不需要模拟整个过程,而是通过数学公式来直接计算出最后剩下的人的编号。

如果我们将问题的规模缩小为n-1,即在一个长度为n-1的圆桌上进行同样的操作,求出的结果为f(n-1, m)。

然后我们将这个结果映射到原始问题的编号上,即将f(n-1, m)变为f(n, m)。

计算f(n, m)的过程如下: - 首先,我们将f(n, m)映射到f(n-1, m)上。

假设f(n, m) = x,那么f(n, m)在映射到f(n-1, m)上的过程中,每次都将整个序列向后移动m 位,即f(n, m)在映射到f(n-1, m)上的过程中,原来的第x个元素变成了第x+m个元素。

因此,f(n-1, m) = (x + m) % n。

- 其次,我们将f(n-1, m)映射回f(n, m)上。

约瑟夫问题多种解决方法

约瑟夫问题多种解决方法
1
著名约瑟夫问题一
• 17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了 这样一个故事:15个教徒和15 个非教徒在深海上遇险, 必须将一半的人投入海中,其余的人才能幸免于难,于是 想了一个办法:30个人围成一圆圈,从第一个人开始依次 报数,每数到第九个人就将他扔入大海,如此循环进行直 到仅余15个人为止。问怎样排法,才能使每次投入大海的 都是非教徒。题目中30个人围成一圈,因而启发我们用一 个循环的链来表示。可以使用结构数组来构成一个循环链。 结构中有两个成员,其一为指向下一个人的指针,以构成 环形的链;其二为该人是否被扔下海的标记,为1表示还 在船上。从第一个人开始对还未扔下海的人进行计数,每 数到9时,将结构中的标记改为0,表示该人已被扔下海了。 这样循环计数直到有15个人被扔下海为止
约瑟夫问题的另外一个有名的例子
• 一堆猴子都有编号,编号是1,2,3 ...m , 这群猴子(m个)按照1-m的顺序围坐一圈, 从第1开始数,每数到第N个,该猴子就要 离开此圈,这样依次下来,直到圈中只剩 下最后一只猴子,则该猴子为大王。
• 二. 基本要求: (1) 输入数据:输入 m,n m,n 为整数,n<m (2)中文提示 按照m个猴子,数n 个数的方法,输出为大 王的猴子是几号 ,建立一个函数来实现此 功能
12
• p2->next=p1; • p2=p1; •} • p2->next=head; • return head; •} • struct monkey *findout(struct monkey *start,int
n) •{ • int i; • struct monkey *p; • i=n; • p=start;
• cout<<"----------------------------------------

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
问题描述
• 已知n(<2^15)个人(以编号1,2,…,n 分别表示)围坐在一圆桌上,从编号为k (1≤ k≤ n)的人开始报数,数到m的那个 人出列,他的下一个人又从1开始报数,数 到m的那个人又出列,依此重复,直到圆桌 周围的人全部出列,依次输出最后个整数T(<2^15)表示测 试次数,接着第二到T+1行分别为n,m和k 的值。 例:2 10 2 3
• public void SortArray(int[]a,int n,int m,int k,int g){ • int[] b=new int[n]; • int c=0; • int i=k-2; • while(true){ • for(int j=0;j<m;){ • i=(i+1)%n; • if(a[i]!=0){ • j++; • } • }
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 5 2 2 2 1 4 20 6
数据测试
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人
10 2 3 6 1 5 30 4 7 18 23 26
循环数m
最后出列的3人
8
3 14 6
数据测试
最后出列的3人 总人数n

约瑟夫问题

约瑟夫问题

再看一个例子! 再看一个例子!
17世纪的法国数学家加斯帕在《数目的游戏问 世纪的法国数学家加斯帕在《 世纪的法国数学家加斯帕在 中讲了这样一个故事: 个教徒和 个教徒和15 题》中讲了这样一个故事:15个教徒和 个非教 徒在深海上遇险,必须将一半的人投入海中, 徒在深海上遇险,必须将一半的人投入海中,其 余的人才能幸免于难,于是想了一个办法:30个 余的人才能幸免于难,于是想了一个办法:30个 人围成一圆圈,从第一个人开始依次报数, 人围成一圆圈,从第一个人开始依次报数,每数 到第九个人就将他扔入大海, 到第九个人就将他扔入大海,如此循环进行直到 仅余15个人为止 问怎样排法, 个人为止。 仅余 个人为止。问怎样排法,才能使每次投入 大海的都是非教徒。 你知道怎么求吗? 大海的都是非教徒。——你知法) 头插法
linklist josephusinserthead(linklist &head) { linklist p,r; int j,no,code; cout<<"请输入循环链表的编号和密码 请输入循环链表的编号和密码!"; 请输入循环链表的编号和密码 cin>>no; cin>>code; while(no!=-1) {p = (linklist)malloc(sizeof(lnode)); p->no=no; p->code=code; p->next = head->next; head->next =p; cout<<"请输入循环链表的编号和密码 请输入循环链表的编号和密码!"; 请输入循环链表的编号和密码 cin>>no; cin>>code; }
约瑟夫问题的一般形式

约瑟夫问题

约瑟夫问题

约瑟夫问题约瑟夫问题约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。

例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。

最后剩下1号。

假定在圈子里前K个为好人,后K个为坏人,你的任务是确定这样的最少M,使得所有的坏人在第一个好人之前被杀掉。

举个例子:有64名战士被敌人俘虏了。

敌人命令他们拍成一圆圈,编上号码1,2,3…,64。

敌人把1号杀了,又把3号杀了,他们隔着一个杀一个这样转着圈杀。

最后只剩下一个人,这个人就是约瑟夫斯。

请问约瑟夫斯是多少号?(这就是“约瑟夫斯”问题。

)这个问题解答起来比较简单:敌人从1号开始,隔一个杀一个,第一圈把所有的奇数号码的战士圈杀光了。

剩下的32名战士需要重新编号,而敌人在第二圈杀死的是重新编号的奇数号码。

由于第一圈剩下的全部是偶数号2,4,6,…,64。

把它们全部用2去除,得1,2,3,…,32。

这是第二圈编的号码。

第二圈杀过以后,又把奇数号码都杀掉了,还剩16个人。

如此下去,可以想到最后剩下的必然是64号。

$64=2^6$,它可以连续被2整除6次,是从1到64中能被2整除次数最多的数,因此,最后必然把64 号留下。

如果有65名战士被俘,敌人还是按上述的方法残杀战士,最后还会剩下约瑟夫斯吗?经过计算,很容易得到结论,不是。

因为第一个人被杀后,也就是1号被杀后,第二个被杀的是必然3号。

如果把1号排除在外,那么还剩下的仍是64人,新1号就是3号。

这样原来的2号就变成了新的64 号,所以剩下的必然是2号。

进一步的归类,不难发现如果原来有$2^k$个人,最后剩下的必然$2^k$号;如果原来有$2^k+1$个人,最后剩下2号;如果原来有$2^k+2$个人,最后剩下4号……如果原来有$2^k+m$个人,最后剩下2m号.比如:原来有100人,由于$100=64+36=2^6+36$,所以最后剩下的就是36×2=72号;又如:原来有11 1人,由于$100=64+47=2^6+47$,所以最后剩下的就是47×2=94号传说古代有一批人被蛮族俘虏了,敌人命令他们排成圆圈,编上号码1,2,3,…然后把1号杀了,把3号杀了,总之每隔一个人杀一个人,最后剩下一个人,这个人就是约瑟夫斯。

约瑟夫问题

约瑟夫问题

倒推问题!
例:n=5, m=3
(0+3) mod 5 = 3 5:0(a),1(b),2(c),3(d),4(e) 2 号 c 是5个人中第一个出列 剩下4个人从3(d)开始报数,环为:3(d),4(e),0(a),1(b) 4:0(d),1(e),2(a),3(b) (1+3) mod 4 = 0 2 号 a 是4个人中第一个出列 剩下3个人从3(b)开始报数,环为:3(b),0(d),1(e) 3:0(b),1(d),2(e) (1+3) mod 3 = 1 2 号 e 是3个人中第一个出列 剩下2个人从0(b)开始报数,环为:0(b),1(d) 2:0(b),1(d) (0+3) mod 2 = 1 0 号 b 是2个人中第一个出列 剩下1个人从1(d)开始报数,环为:1(d) 0 1:0(d) 0 号 d 是1个人中第一个出列,也是最后一个出列。
n 个人构成一个环, 0, 1, 2, …, n-2, n-1 称为 n元约瑟夫环。 从 0 开始报数,假设 x 是最后一个出列的人的编号。
假设 k = m mod n 第一个出列的人编号为 k-1(当元素出现在边界时特殊处理) 0, 1, 2,…, k-2, k-1, k, k+1,…, n-2, n-1 他出列之后,剩下的 n-1 个人也构成了一个(n-1)元环: k, k+1,…, n-2, n-1, 0, 1, 2,…, k-2 从 k 开始报 0。 这 n-1 个人最后一个出列的人也是 x
// 出列n-1个人
/* 初始化状态数组 */ for i <- 1 to n step 1 do person[i] <- 1 /* n-1 个人出列 */ i <- 0 for p <- 1 to n-1 step 1 do begin j <- 0 while ( j < m ) do begin i <- (i mod n) + 1 if ( person[i] <> 0 ) then j <- j + 1 end person[i] <- 0 end /* 查找没有出列的人 */ i <- 1 while ( person[i] = 0 ) do i <- i+1 write( i )

约瑟夫问题

约瑟夫问题

源程序: Program ex2; type nodep=^node; node=record data:integer; nest:nodep end; var head,p,q:nodep; m,n,I:integer; begin write(‘m&n:’);readln(m,n);{构造环——循 环链表}
第二种方法
分析用循环链表解决问题,首先需要构造一个循环链表,构造循环链表的方法很简单, 分析用循环链表解决问题,首先需要构造一个循环链表,构造循环链表的方法很简单, 只要将最后一个人的下一个指针指向第一个人,这样就构成一个环,如图所示, 只要将最后一个人的下一个指针指向第一个人,这样就构成一个环,如图所示,构造循环链 表以后,就可以进行删除操作,直到循环链表剩下一个人为止。 表以后,就可以进行删除操作,直到循环链表剩下一个人为止。
约瑟夫问题揭秘 Josephus Problem
Who can live at last?
约瑟夫问题的来历
据说著名犹太历史学家 Josephus有过以下的故事:在罗马 人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲 到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到, 于是决定了一个自杀方式,41个人排成一个圆圈,由第1 个人开始报数,每报数到第3人该人就必须自杀,然后再 由下一个重新报数,直到所有人都自杀身亡为止。然而 Josephus 和他的朋友并不想遵从,Josephus要他的朋友先 假装遵从,他将朋友与自己安排在第16个与第31个位置, 于是逃过了这场死亡游戏。
源程序: Program ex4; const max=100; var p:array[1..max] of integer; s1,I,j,k,w,m,n,s:integer;

约瑟夫问题

约瑟夫问题

用循环队列解决约瑟夫问题
#include <iostream.h> void Fmade(int x, int y, int z); void main() { int a, b, c; //t i, j, k; //t aa[41], b[41]; cout<<"请输入总人数:"; 请输入总人数: 请输入总人数 cin>>a; cout<<endl<<"请输入开始位子:"; 请输入开始位子: 请输入开始位子 cin>>b; cout<<endl<<"请输入步长:"; 请输入步长: 请输入步长 cin>>c; Fmade(a, b, c); }
void Fmade(int x, int y, int z) { int i, j=0, k=0; int aa[41], bb[41]; int start; aa[0]=0; for(i=1; i<=x; i++) { aa=i; } start=y; while(j < x) { while(start <= x) { if(aa[start] !=0) { k++; }
问题分析与算法设计
41个人围成一圈,因而启发我们用一个循环的链来表 个人围成一圈, 个人围成一圈 可以使用结构数组来构成一个循环链。 示。可以使用结构数组来构成一个循环链。结构中有两个 成员,其一为指向下一个人的指针,以构成环形的链; 成员,其一为指向下一个人的指针,以构成环形的链;其 二为该人是否自杀的标记, 表示还活着。 二为该人是否自杀的标记,为1表示还活着。从第一个人 表示还活着 开始对还未自杀的人进行计数,每数到3时 开始对还未自杀的人进行计数,每数到 时,将结构中的 标记改为0,表示该人已自杀了。这样循环计数直到有2个 标记改为 ,表示该人已自杀了。这样循环计数直到有 个 人还活着为止。 人还活着为止。

3约瑟夫斯问题

3约瑟夫斯问题

工程管理科内控制度第一部分:绪论工程管理是一门综合性学科,涉及范围广泛,工程管理科内控制度是保障项目实施质量和效率的重要保障措施。

合理的内控制度可以有效地规范工程管理活动,提高工程管理水平,确保项目进展顺利。

本文将从内控制度的概念、内控制度的重要性、内控制度的基本要素和内控制度的实施过程等方面进行探讨。

第二部分:内控制度的概念内控制度是指一个企业或组织为了达成其目标而制订的控制措施和程序。

在工程管理科领域,内控制度是为了规范与管理项目管理活动而制订的一系列内部控制规定和程序。

内控制度的实施可以有效地防范风险,提高工作效率,保障工程管理的质量和安全。

第三部分:内控制度的重要性工程管理科内控制度的建立和实施对于确保项目的顺利进行至关重要。

首先,内控制度可以规范管理流程,明确责任分工,提高工作效率。

其次,内控制度可以有效防范风险,降低项目管理风险,确保项目按期、按质完成。

再次,内控制度可以提高工程管理科的整体形象,增强内部员工的工作积极性和责任心。

总之,内控制度的建立和实施对于保障工程管理科的顺利运转和发展至关重要。

第四部分:内控制度的基本要素内控制度的建立和实施包括以下几个基本要素:1. 控制目标:明确工程管理科的目标和任务,确立明确的工作目标和目标任务。

2. 控制环境:建立健全的内部环境,包括组织结构、内部管理制度、企业文化等。

3. 风险评估:对工程管理科所面临的各种风险进行评估和分析,制定相应的风险应对策略。

4. 控制活动:制定具体的控制措施和程序,规范工程管理活动的开展和执行。

5. 信息与沟通:建立健全的信息系统和内部沟通机制,确保信息的畅通和及时传达。

6. 监督和评价:建立监督和评价机制,对内控措施和程序进行监督和检查,及时发现问题并加以解决。

第五部分:内控制度的实施过程内控制度的实施是一个系统性和持续性的过程,需要在全体员工共同努力下不断推进。

具体实施过程如下:1. 制定内控制度规定:在工程管理科领导的指导下,建立内控制度规定,明确制度内容和执行规范。

约瑟夫斯问题

约瑟夫斯问题

约瑟夫斯问题约瑟夫问题维基百科,⾃由的百科全书跳到导航跳到搜索约瑟夫问题(有时也称为约瑟夫斯置换),是⼀个出现在计算机科学和数学中的问题。

在计算机编程的算法中,类似问题⼜称为约瑟夫环。

⼈们站在⼀个等待被处决的圈⼦⾥。

计数从圆圈中的指定点开始,并沿指定⽅向围绕圆圈进⾏。

在跳过指定数量的⼈之后,处刑下⼀个⼈。

对剩下的⼈重复该过程,从下⼀个⼈开始,朝同⼀⽅向跳过相同数量的⼈,直到只剩下⼀个⼈,并被释放。

问题即,给定⼈数、起点、⽅向和要跳过的数字,选择初始圆圈中的位置以避免被处决。

历史这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的⼀名犹太历史学家。

他在⾃⼰的⽇记中写道,他和他的40个战友被罗马军队包围在洞中。

他们讨论是⾃杀还是被俘,最终决定⾃杀,并以抽签的⽅式决定谁杀掉谁。

约瑟夫斯和另外⼀个⼈是最后两个留下的⼈。

约瑟夫斯说服了那个⼈,他们将向罗马军队投降,不再⾃杀。

约瑟夫斯把他的存活归因于运⽓或天意,他不知道是哪⼀个。

[1]解法⽐较简单的做法是⽤循环单链表模拟整个过程,时间复杂度是O(n*m)。

如果只是想求得最后剩下的⼈,则可以⽤数学推导的⽅式得出公式。

且先看看模拟过程的解法。

Python版本-- coding: utf-8 --class Node(object):def init(self, value):self.value = valueself.next = Nonedef create_linkList(n):head = Node(1)pre = headfor i in range(2, n+1):newNode = Node(i)pre.next= newNodepre = newNodepre.next = headreturn headn = 5 #总的个数m = 2 #数的数⽬if m == 1: #如果是1的话,特殊处理,直接输出print (n)else:head = create_linkList(n)pre = Nonecur = headwhile cur.next != cur: #终⽌条件是节点的下⼀个节点指向本⾝for i in range(m-1):pre = curcur = cur.nextprint (cur.value)pre.next = cur.nextcur.next = Nonecur = pre.nextprint (cur.value)using namespace std;typedef struct _LinkNode {int value;struct _LinkNode* next;} LinkNode, *LinkNodePtr;LinkNodePtr createCycle(int total) {int index = 1;LinkNodePtr head = NULL, curr = NULL, prev = NULL;head = (LinkNodePtr) malloc(sizeof(LinkNode));head->value = index;prev = head;while (--total > 0) {curr = (LinkNodePtr) malloc(sizeof(LinkNode));curr->value = ++index;prev->next = curr;prev = curr;}curr->next = head;return head;}void run(int total, int tag) {LinkNodePtr node = createCycle(total);LinkNodePtr prev = NULL;int start = 1;int index = start;while (node && node->next) {if (index == tag) {printf("%d\n", node->value);prev = node->next;node->next = NULL;node = prev;} else {prev->next = node->next;node->next = NULL;node = prev->next;}index = start;} else {prev = node;node = node->next;index++;}}}int main() {if (argc < 3) return -1;run(atoi(argv[1]), atoi(argv[2]));return 0;}数学推导解法我们将明确解出{\displaystyle k=2}k=2时的问题。

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析Josephu问题为:设置编号为1,2,3,......n的n个⼈围坐⼀圈,约定编号为k(1<=k<=n)的⼈从1看是报数,数到m的那个⼈出列,它的下⼀位⼜从1开始报数,数到m的那个⼈出列,以此类推,直到所有⼈出列为⽌,由此产⽣⼀个出队编号的序列。

提⽰:⽤有个不带头的循环链表来处理Josephu问题:先构成⼀个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下⼀个结点⼜从1开始计数,直到最后⼀个结点从链表中删除算法结束。

代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(5);cycLink.createLink();cyclink.show();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;//设置链表⼤⼩public void setLen(int len){this.len=len;}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}优化:代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(50);cycLink.createLink();cycLink.setK(2);cycLink.setM(3);cyclink.show();cyclink.play();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了 Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;int k=0;int m=0;//设置mpublic void setM(int m){this.m=m;}//设置链表⼤⼩public void setLen(int len){this.len=len;}//设置从第⼏个⼈开始数数public void setK(int k){this.k=k;}//开始playpublic void play(){Child temp=this.fistChild;//1.先找到开始数数的⼈//int i=1;i<k;因为⾃⼰也要数⼀下,所以i不能为k for(int i=1;i<k;i++){temp=temp.nexChild;}while(this.len!=1){//2.数m下for(int j=1;j<m;j++){temp=temp.nextChild;}//找到要出圈的前⼀个⼩孩,有待优化Child temp2=temp;while(temp2.nextChild!=temp){temp2=temp2.nextChild;}//3.将数到m的⼩孩,退出圈temp2.nextChild=temp.nextChild;//让temp指向数数的⼩孩temp=temp.nextChild;this.len--;}//最后⼀个⼩孩(验证)System.out.println(temp.no);}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦ temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.循环单链表求解约瑟夫问题。

#include<stdio.h>#include<stdlib.h>typedef int DataType;typedef struct node{DataType data;struct node *next;}LNode,*LinkList;LinkList Creat_josephus_LinkList(int n){LinkList h,p,s;DataType x;int i;h=p=(LinkList)malloc(sizeof(LNode));s=h;printf("请输入第1个数据元素:");scanf("%d",&x);s->data=x;s->next=NULL;for(i=1;i<n;i++){printf("请输入第%d个数据元素:",i+1);scanf("%d",&x);s=(LinkList)malloc(sizeof(LNode));s->data=x;s->next=NULL;p->next=s;p=s;}s->next=h;return(h);}int josephus_LinkList(LinkList josephus_Link,int weizhi,int jiange){LinkList p,pre;int i;if(!josephus_Link){printf("表中无元素!");return 0;}p=josephus_Link;for(i=1;i<weizhi;i++)p=p->next;printf("输出约瑟夫序列:");while(p!=p->next){for(i=1;i<jiange;i++){pre=p;p=p->next;}printf("%d\t",p->data);pre->next=p->next;free(p);p=pre->next;}printf("%d\n",p->data);free(p);return 1;}void main(){int weizhi,jiange,n;LinkList josephus_Link;printf("请输入数据元素的个数:");scanf("%d",&n);josephus_Link=Creat_josephus_LinkList(n);printf("请输入开始节点的位置:");scanf("%d",&weizhi);printf("请输入报数间隔大小:");scanf("%d",&jiange);josephus_LinkList(josephus_Link,weizhi,jiange);getchar();}2.顺序表求解约瑟夫问题。

#include<stdio.h>#include<stdlib.h>#define MAXSIZE 100typedef int DataType;typedef struct node{DataType data[MAXSIZE];int length;}SeqList,*PSeqList;PSeqList Creat_josephus_seq(){int i;PSeqList PL;PL=(PSeqList)malloc(sizeof(SeqList));if(PL)PL->length=0;printf("请输入线性表的长度:");scanf("%d",&(PL->length));for(i=0;i<PL->length;i++){printf("数据%d=",i);scanf("%d",&(PL->data[i]));}return(PL);}int Delete_SeqList(PSeqList PL,int i){int j;if(!PL){ printf("表不存在!"); return(-1); }if(i<1||i>PL->length){ printf("删除位置错误!");return 0; }for(j=i;j<PL->length;j++)PL->data[j-1]=PL->data[j];PL->length--;return(1);}int josephus_SeqList(PSeqList josephus_seq,int weizhi,int jiange){int s1,i;if(!josephus_seq->length){ printf("表中无元素!"); return 0; }s1=weizhi-1;printf("输出约瑟夫序列:");for(i=josephus_seq->length;i>0;i--){s1=(s1+jiange-1)%i;printf("%d\t",josephus_seq->data[s1]);Delete_SeqList(josephus_seq,s1+1);}printf("\n");return 1;}void main(){int weizhi,jiange;PSeqList josephus_seq;josephus_seq=Creat_josephus_seq();printf("请输入开始节点的位置:");scanf("%d",&weizhi);printf("请输入报数间隔:");scanf("%d",&jiange);josephus_SeqList(josephus_seq,weizhi,jiange);getchar();}3.静态链表求解约瑟夫问题。

#include<stdio.h>#include<stdlib.h>#define MAXSIZE 100 typedef struct{int data;int next;}SNode;SNode sp[MAXSIZE]; static int SL=0;static int length;Creat_josephus_stlist(){int i,d;for(i=0;i<length;i++){printf("数据%d:",i+1);scanf("%d",&d);sp[i].data=d;sp[i].next=i+1;}sp[length-1].next=0;}void out_josephus_stlist(int length){int i;printf("xiaobiao data next\n");for(i=0;i<length;i++)printf("%d\t%d\t%d\n",i,sp[i].data,sp[i].next); }int josephus_StList(int weizhi,int jiange){int p,pre,temp,i;p=SL;pre=SL;Creat_josephus_stlist(length);out_josephus_stlist(length);for(i=1;i<weizhi;i++)p=sp[p].next;printf("输出约瑟夫序列:\n");while(p!=sp[p].next){for(i=1;i<jiange;i++){pre=p;p=sp[p].next;}printf("%d\t",sp[p].data);temp=sp[pre].next;sp[pre].next=sp[p].next;sp[p].next=temp;p=sp[pre].next;}printf("%d\n",sp[p].data);return 1;}void main(){int weizhi,jiange;printf("请输入静态链表的长度:");scanf("%d",&length);printf("请输入开始节点的位置:");scanf("%d",&weizhi);printf("请输入报数间隔:");scanf("%d",&jiange);josephus_StList(weizhi,jiange);getchar();}。

相关文档
最新文档