题目:
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号0,1,2,3…n-1分别表示)围坐在一张圆桌周围。从编号为0的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,求最后一个出列的人。

1.经典解法

可以用链表来模拟约瑟夫环,每次在链表中删除第m个节点,然后不断,直至链表中只剩下一个节点。最后一个这个节点就是我们要求的节点。
注意:当迭代器扫描到链表末尾时,将迭代器移至链表头。

int lastRemaining(unsigned int n, unsigned int m){if(n<1||m<1)return -1;list<int> numbers;          //单链表模拟约瑟夫环(Josephus Ring)for(int i=0;i<n;++i)numbers.push_back(i);list<int>::iterator cur=numbers.begin(); //从第一个节点开始报数while(numbers.size()!=1){                //循环迭代至约瑟夫环只剩最后一个元素for(int i=0;i<m-1;++i){              //迭代m-1次找到需要移出环的元素++cur;if(cur==numbers.end())           //迭代器扫描到链表末尾时,将迭代器移至链表头cur=numbers.begin();}list<int>::iterator next=++cur;if(next==numbers.end())next=numbers.begin();--cur;numbers.erase(cur);cur=next;}return *cur;
}

分析:经典解法易于理解,实现简单。但是重复遍历降低效率,没删除一个元素需要m步移动,对n个元素,时间复杂度为O(mn)。

2.动态规划求解

动态规划重点是要确定状态和状态转移方程。状态就是确定问题的解的表达式,状态转移方程就是上一阶段问题的解推出当前阶段问题解的递推式。

针对约瑟夫环问题,n个元素的环可以定义为f(n,m),m表示每次移动的步数。则状态就是f(n,m)。那么对于n-1个元素的环,其最后一个被移除的元素就是f(n-1,m)。当环中只有一个元素时,f(1,m)=0。那么这里的重点和难点就是找出f(n,m)和f(n-1,m)之间的关系。也就是状态转移方程。

那么看如下分析:
(1)第一个被删除的数为 (m - 1) % n,设下标为k。
(2)假设第二轮的开始元素下标为k+1,那么这n - 1个数构成的约瑟夫环为k + 1, k + 2, k +3, …..,k - 3, k - 2,k-1。做一个简单的映射。
k+1 —> 0
k+2 —> 1

n-1 —> n-k-2
0 —> n-k-1

k-1 —>n-2
可见,映射关系为p(x)=(x-k-1)%n,那么其从右到左的映射关系是p−1(x)=(x+k+1)%np^{-1}(x)=(x+k+1)\%n。因为右边最后出列的元素下标是f(n-1,m),映射到左边就是p−1(f(n−1,m))=(f(n−1,m)+k+1)%np^{-1}(f(n-1,m))=(f(n-1,m)+k+1)\%n,又因为k=(m-1)%n,代入上面式中得:
p−1(f(n−1,m))=(f(n−1,m)+k+1)%n=(f(n−1,m)+m)%np^{-1}(f(n-1,m))=(f(n-1,m)+k+1)\%n=(f(n-1,m)+m)\%n,即:

f(n,m)={0(f(n−1,m)+m)%nn=1n>1

f(n,m)=\begin{cases} 0&\text{n=1}\\ (f(n-1,m)+m)\%n & \text{n>1}\end{cases}

有了状态转移方程,我们可以使用迭代或者递归来完成问题求解。建议迭代,以免对不同阶段的问题重复求解。

下面以迭代方式实现:

int lastRemainingDP(unsigned int n, unsigned int m){if(n<1||m<1)return -1;int last=0; //n=1,最后移出环的元素for(int i=2;i<=n;++i)last=(last+m)%i;return last;
}

参考文献

[1]剑指Offer.何海涛.电子工业出版社.
[2]解题笔记(10)——约瑟夫环问题.

动态规划解决约瑟夫环问题相关推荐

  1. PHP解决约瑟夫环问题

    PHP解决约瑟夫环问题 一.总结 二.PHP解决约瑟夫环问题 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到 ...

  2. 循环链表解决约瑟夫环问题

    约瑟夫环问题可以简单的使用数组的方式实现,但是现在我使用循环链表的方法来实现,因为上午看到一道面试题规定使用循环链表解决约瑟夫环问题. 什么是约瑟夫环? "约瑟夫环是一个数学的应用问题:已知 ...

  3. 用java解决约瑟夫循环问题,Java实现循环列表解决约瑟夫环问题

    约瑟夫环:共有n个人围成一圈,从1开始报数,数到m的人出圈,求最后幸运者序号?? 下面用Java实现循环列表解决这个问题: package com.iteye.ljmdbc7a; import jav ...

  4. java实现环形链表解决约瑟夫环问题

    什么是环形链表? 环形链表就是单向链表的基础上让链表的首尾相连,形成一个环,这就是一个循环链表. 什么是约瑟夫环问题? 约瑟夫环如下: 约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数, ...

  5. C语言使用数组和循环解决约瑟夫环问题

    C语言使用数组和循环解决约瑟夫环问题 约瑟夫入狱,监狱内共有 33 个犯人.某日 33 名犯人围成一圈,从第一个犯人开始报数,报到数字 7 的犯人出列, 被枪毙,下一名犯人重新从 1 开始报数.依次类 ...

  6. 【图解经典算法题】如何用一行代码解决约瑟夫环问题

    约瑟夫环问题算是很经典的题了,估计大家都听说过,然后我就在一次笔试中遇到了,下面我就用 3 种方法来详细讲解一下这道题,最后一种方法学了之后保证让你可以让你装逼. 问题描述:编号为 1-N 的 N 个 ...

  7. python解决约瑟夫环(杀人游戏)

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

  8. 使用队列解决约瑟夫环问题

    题目描述: 首先,让小朋友们围成一个大圈.然后,随机指定一个数m,让编号为0的小朋友开始报数.每次喊到m-1的那个小朋友要出列唱首歌,并且不再回到圈中,从他的下一个小朋友开始,继续0-m-1报数-这样 ...

  9. 从农夫砍大白菜到解决约瑟夫环的问题!

    先简单说一下 农夫砍白菜的问题. 在一个院子里农夫中了一圈n颗大白菜,到了收货的季节.,作为一名有理想的农夫决定没数三个数就砍掉一颗,然后接着开始数三个数继续.. 问 砍掉头m颗白菜的顺序是什么? 这 ...

最新文章

  1. Weblogic禁用SSLv3和RC4算法教程
  2. rsync 常见错误与解决方法整理
  3. VeriSign SSL证书apache安装
  4. Java 8 的 JVM 有多快?Fork-Join 性能基准测试
  5. 前端开发使用工具 gulp
  6. 【蓝桥杯Java_C组·从零开始卷】第三节(附)、for循环练习题(数据题与图形题)
  7. This will have no impact if delete.topic.enable is not set to true以及删除kafka中的topic
  8. runloop解决Cell上主线程卡顿
  9. mysql常见错误解决方法_mysql常见错误解决办法
  10. Doris之动态分区(全面)
  11. 年会活动现场抽奖签到互动小程序开发制作
  12. mysql索引左倾_MySQL索引学习
  13. 理解 this.initialize.apply ( this, arguments )
  14. 抓包工具 - HttpWatch(功能详细介绍)
  15. python动物农场小说网站爬虫_中文编程,用python编写小说网站爬虫
  16. 怎么写竞品分析报告(思路):
  17. 中科院基于gpt的学术优化网站搭建教程
  18. Poi读取大数据量Excel文件
  19. Multisim10在51单片机仿真中的运用
  20. C语言实现栈(附带题目讲解)

热门文章

  1. Android扩大点击事件接收区域范围
  2. 第一章 Spring的简单剖析
  3. 前端开发学习之——dom ready和window onload的区别
  4. python自动化运维之路~DAY1
  5. Qt、GTK 和KDE、GNOME的关系-转
  6. Linux调试工具strace和gdb常用命令小结-转
  7. python进程数上限_在多处理python中限制进程数
  8. Java 创建线程的4种方式
  9. 蓝桥杯 ALGO-46 算法训练 Hanoi问题
  10. [Java] 蓝桥杯ALGO-146 算法训练 4-2找公倍数