问题描述:皇帝决定找出全国中最幸运的一个人,于是从全国选拔出 n 个很幸运的人,让这 n 个人围着圆桌进餐,可是怎么选择出其中最幸运的一个人呢?皇帝决定:从其中一个人从 1 开始报数,按顺序数到第 k 个数的人自动出局,然后下一个人从 1 开始报数,数到 k 的人出局……。如此直到最后只剩下约瑟夫一人,然后他就成为全国最幸运的人。请问约瑟夫最初的位置?(注:原问题略显暴力,故自创此趣味题目)

分析:把第一个开始报 1 的人标定为 1,然后按报数顺序依次标定其余的人为:2,3,……,n - 1,n。按规则进行淘汰,直到最后剩一个数字,这个数字就是约瑟夫的位置。

解决方案:

1. 模拟法(simulation)

数组模拟,时间复杂度最高为 O(n3) (当k≈n时 ) ,空间复杂度O(n)

 /********** 用数组模拟 *************/
void findNext(bool *out, int n, int &curPosition){
if(!out[curPosition]) {
int pNext = (curPosition + ) % n;
if(!out[pNext])
curPosition = pNext;
else{
curPosition = pNext;
while(out[curPosition])
curPosition = (curPosition + ) % n;
}
}else
{
while(out[curPosition])
curPosition = (curPosition + ) % n;
}
}
int josephus(int n, int k)
{
if(n < || k < ) return -;
if(n == ) return n;
bool *out = new bool[n]; /********* 记录是否出局 *********/
for(int i = ; i < n; ++i)
out[i] = false;
int current = ;
int n2 = n;
while(n2 != )
{
int cnt = k;
while(--cnt)
findNext(out, n, current);
out[current] = true;
findNext(out, n, current);
--n2;
}
delete[] out;
out = NULL;
return (current + );
}

Code

循环链表模拟:时间复杂度O(n),空间复杂度O(n)

/********** 循环链表 *************/
struct ListNode{
int val;
ListNode * next;
ListNode(int x):val(x), next(NULL) {}
}; int josephus(int n, int k)
{
if(n < 1 || k < 1)
return -1;
if(n == 1) return n;
ListNode *head = new ListNode(1);
ListNode *prior = head;
for(int i = 2; i <= n; ++i)
{
ListNode *tem= new ListNode(i);
prior->next = tem;
prior = prior->next;
}
prior->next = head;
while(head->next != head)
{
int cnt = k;
while(--cnt)
{
head = head->next;
prior = prior->next;
}
prior->next = prior->next->next;
ListNode *current = head;
head = head->next;
delete current; /*** 只释放堆内存空间,局部指针自动回收 ***/
}
return head->val;
}

 2.建模法(modeling)

使用队列建模。

 /********** 用队列(注:使用STL可简单化) *************/
bool ERROR = false;
typedef int ELEM;
struct Node{
ELEM val;
Node *next;
Node(ELEM e):val(e), next(NULL){}
};
struct queue{
queue():front(NULL), tail(NULL) {}
ELEM pop();
void push(ELEM val);
bool empty();
private:
Node *front;
Node *tail; };
ELEM queue::pop(){
if(front == NULL){
ERROR = true;
return -;
}else{
ELEM v = front->val;
front = front->next;
return v;
}
}
void queue::push(ELEM val){
Node *p = new Node(val);
if(front == NULL)
front = tail = p;
else
{
tail->next = p;
tail = tail->next;
}
}
bool queue::empty(){
if(front == NULL)
return true;
else
return false;
} int josephus(int n, int k)
{
if(n < || k < ) return -;
if(n == ) return ;
queue qu;
for(int i = ; i <= n; ++i)
qu.push(i);
int result = ;
while(!qu.empty())
{
for(int i = ; i <= k-; ++i)
qu.push(qu.pop());
result = qu.pop();
}
return result;
}

Code

 

3. 数学推理 && 动态规划

初始:0 1 ... (k-2) (k-1)  k ... (N-1)

K 出局:                    新的顺序:                         

k                              0                                                                      

...            p               ...                                                           

N-1         映              N - k - 1                                                P(x) = (x - k + N) mod N                             

0             射              N - k                                              令:y = P(x) = (x - k + N) mod N                             

1                              N - k + 1                                        则,x = (y + k - N)             mod N  

...                             ...                                                           = (y + k)                   mod N

k-2                           N - 2                                           P-1(x) = (x + k) mod N 

设 f(N,k) 为最后所得的数字,则:

f(N,k) = P-1( f(N-1,k) ) = (f(N-1,k) + k) mod N

所以有如下递推公式:

int josephus(int n, int k)
{
if(n < 1 || k < 1) return -1;
if(n == 1) return 1;
int result = 0;
for(int i = 2; i <= n; ++i)
result = (result + k) % i;
return result+1;
}

另外,简洁的递归:(不推荐,递归栈太小,容易溢出)

int josephus(int n, int k)
{
if(n < 1 || k < 1) return -1;
if(n == 1) return 1;
else
return ((josephus(n - 1, k) + k - 1) % n + 1);
}

最后,当 k = 2 时,如下公式可直接求出:

代码为:

int n = 1000;
cout<< 2*(n - pow(2.0, int(log((float)n) / log((float)2))))+1 <<endl;

约瑟夫(环)问题(Josephus problem)的更多相关文章

  1. 约瑟夫环问题(Josephus)

    约瑟夫环:用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至最后一个元素并输出该元素的值. 一.循环链表:建立一个有N个元素的循环链表,然后从链表头开始遍历并记数,如果计数值为M,则 ...

  2. 组合数学--约瑟夫环问题 Josephus

    约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环. 有n个囚犯站成一个圆圈,准备处决.首先从一个人开始,越过k-2个人(因为第 ...

  3. 约瑟夫问题(Josephus Problem)的两种快速递归算法

    博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/ 对,我是来骗访问量的!O(∩_∩)O~~ 约瑟夫问题(Josephus Problem)也称 ...

  4. LightOJ - 1179 Josephus Problem(约瑟夫环)

    题目链接:https://vjudge.net/contest/28079#problem/G 题目大意:约瑟夫环问题,给你n和k(分别代表总人数和每次要数到k),求最后一个人的位置. 解题思路:因为 ...

  5. 谁能笑到最后,约瑟夫环-Josephus问题求解

     一. 简述Josephus问题 N个人站成一环,从1号开始,用刀将环中后面一个人“消灭“”掉,之后再将刀递给下一个人,这样依次处理,最后留下一个幸存者. 二. 求解方法  1.  约瑟夫问题如果使用 ...

  6. 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)

    /************************************************************************* * * Josephus problem * * ...

  7. Josephus环的四种解法(约瑟夫环)

    约瑟夫环 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个 ...

  8. Josephus problem(约瑟夫问题,丢手绢问题)

    约瑟夫问题 约瑟夫环问题是一个数学应用题:已知n个人(以编号1,2,3.....,n)围坐在一张圆桌的周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列 ...

  9. 单向环形链表解决约瑟夫环(Josephus)问题

    一.约瑟夫环问题 Josephu 问题为:设编号为1,2,- n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那 ...

  10. Roman Roulette(约瑟夫环模拟)

    Roman Roulette Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

随机推荐

  1. 最小割 总结&&做题记录

    模型要点: 1.一般适用于二取一问题或者01规划. 2.利用最小割=最大流,转化为最大流求之. 建议阅读胡伯涛的论文 <<最小割模型在信息学竞赛的应用>>,有精彩有序的证明和各 ...

  2. vertical-align及IE7下的inline-block

    在IE7下,是不支持inline-block元素的,当对块级元素如dl进行inline-block样式设置时,在IE7浏览器是下样式是不会生效的. 若要在IE7下实现将块级元素设置为内联元素,可以这样 ...

  3. Struts2中实现Web项目的初始化工作

    Struts2中实现Web项目的初始化工作 注:通常web系统在启动时需要做一些初始化的工作,比如初始化系统全局变量,加载自定义配置文件,启动定时任务等.  一.在Struts中实现系统的初始化工作 ...

  4. 分析器错误 MvcApplication 找不到

    <%@ Application Codebehind="Global.asax.cs" Inherits="test.MvcApplication" La ...

  5. springboot系列之-profile

    Spring Boot profile用于分离不同环境的参数配置,通过spring.profile.active参数进行设置. 在Spring Boot中应用程序配置可以使用2种格式:applicat ...

  6. PAT (Basic Level) Practise:1012. 数字分类

    [题目链接] 给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字: A1 = 能被5整除的数字中所有偶数的和: A2 = 将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2+n3- ...

  7. PAT (Basic Level) Practise:1032. 挖掘机技术哪家强

    [题目链接] 为了用事实说明挖掘机技术到底哪家强,PAT组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第1行给出不超过105的正整数N,即参赛人数.随后N行 ...

  8. 通过printf设置Linux终端输出的颜色和显示方式

    转载自:http://www.cnblogs.com/clover-toeic/p/4031618.html 在Linux终端下调试程序时,有时需要输出大量信息.若能控制字体的颜色和显示方式,可使输出 ...

  9. CentOS 6.5设置静态IP教程 并且可以ping通

    CentOS6.5掉电或重启,它的IP会被DHCP重新分配,如果要远程控制这台电脑,不得不去打开显示器去查看它的新IP,这样太麻烦了.于是需要将这台电脑的IP设置成静态的. 网上常规的设置静态ip的方 ...

  10. javascript的document.execCommand(转)

    document.execCommand()方法处理html数据时常用语法格式如下: 代码: document.execCommand(sCommand[,交互方式, 动态参数]) 其 中:sComm ...