约瑟夫(环)问题(Josephus problem)
问题描述:皇帝决定找出全国中最幸运的一个人,于是从全国选拔出 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)的更多相关文章
- 约瑟夫环问题(Josephus)
约瑟夫环:用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至最后一个元素并输出该元素的值. 一.循环链表:建立一个有N个元素的循环链表,然后从链表头开始遍历并记数,如果计数值为M,则 ...
- 组合数学--约瑟夫环问题 Josephus
约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环. 有n个囚犯站成一个圆圈,准备处决.首先从一个人开始,越过k-2个人(因为第 ...
- 约瑟夫问题(Josephus Problem)的两种快速递归算法
博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/ 对,我是来骗访问量的!O(∩_∩)O~~ 约瑟夫问题(Josephus Problem)也称 ...
- LightOJ - 1179 Josephus Problem(约瑟夫环)
题目链接:https://vjudge.net/contest/28079#problem/G 题目大意:约瑟夫环问题,给你n和k(分别代表总人数和每次要数到k),求最后一个人的位置. 解题思路:因为 ...
- 谁能笑到最后,约瑟夫环-Josephus问题求解
一. 简述Josephus问题 N个人站成一环,从1号开始,用刀将环中后面一个人“消灭“”掉,之后再将刀递给下一个人,这样依次处理,最后留下一个幸存者. 二. 求解方法 1. 约瑟夫问题如果使用 ...
- 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)
/************************************************************************* * * Josephus problem * * ...
- Josephus环的四种解法(约瑟夫环)
约瑟夫环 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个 ...
- Josephus problem(约瑟夫问题,丢手绢问题)
约瑟夫问题 约瑟夫环问题是一个数学应用题:已知n个人(以编号1,2,3.....,n)围坐在一张圆桌的周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列 ...
- 单向环形链表解决约瑟夫环(Josephus)问题
一.约瑟夫环问题 Josephu 问题为:设编号为1,2,- n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那 ...
- Roman Roulette(约瑟夫环模拟)
Roman Roulette Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)To ...
随机推荐
- java编码转换 unicode to utf-8
private String decodeUnicode(String theString) { char aChar; int len = theString.length(); StringBuf ...
- candence 知识积累2
1 Allegro Symbol的类型以及作用: (1)Package Symbol : PCB里的封装符号,元器件的footprint,用来做元器件的封装,后缀(.psm),主要在电器层Etch ( ...
- PAT (Basic Level) Practise:1037. 在霍格沃茨找零钱
[题目链接] 如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易 ...
- LeetCode 【190. Reverse Bits】
Reverse bits of a given 32 bits unsigned integer. For example, given input 43261596 (represented in ...
- 渴切API参考手册
渴切:是国内优秀的开源css框架. 渴切是一个开源中文 (X)HTML/CSS 框架 ,它的目的是减少你的css开发时间.它提供一个可靠的css基础去创建你的项目,能够用于网站的快速设计,通过重设和重 ...
- Django 的开始之路
django源码安装 检查是否安装成功 1.在命令行进入Django环境 在新建完项目后 进入带有manage.py的目录,执行以下文件. python manage.py shell 2.Djan ...
- ps磨皮
光滑磨皮步骤: 1.用高斯模糊滤镜模糊皮肤,用蒙版控制范围,去掉较为明显的杂色及瑕疵.可以高斯模糊重复多次,去掉明显的杂色. 2.用涂抹工具处理细小的瑕疵及加强五官等部位的轮廓: 3.整体美白及润色 ...
- Python学习笔记——Day5(转载)
python 编码转换 主要介绍了python的编码机制,unicode, utf-8, utf-16, GBK, GB2312,ISO-8859-1 等编码之间的转换. 常见的编码转换分为以下几种情 ...
- 使用seajs封装js模块
//方法一:将函数绑定到原型上 define(function(require, exports, module) { $.fn.tab = function(option, callback) { ...
- centos6.4_安装Python3.5.2之问题
一.安装centos6.4虚拟机 这个就不用我详细介绍了,网上安装教程一大把了哈,自己百度安装应该没啥问题了 二.下载python安装包 官网下载python3.5.2安装包:https://www. ...