链接


牛客OJ:孩子们的游戏(圆圈中最后剩下的数)

九度OJ:http://ac.jobdu.com/problem.php?pid=1356

GitHub代码: 045-孩子们的游戏(圆圈中最后剩下的数)

CSDN题解:剑指Offer–045-孩子们的游戏(圆圈中最后剩下的数)

牛客OJ 九度OJ CSDN题解 GitHub代码
045-孩子们的游戏(圆圈中最后剩下的数) 1356-孩子们的游戏(圆圈中最后剩下的数) 剑指Offer–045-孩子们的游戏(圆圈中最后剩下的数) 045-孩子们的游戏(圆圈中最后剩下的数)

题意


题目描述

每年六一儿童节,NowCoder都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。

HF作为NowCoder的资深元老,自然也准备了一些小游戏。

其中,有个游戏是这样的:

首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。

每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,

并且拿到NowCoder名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。

请你试着想下,哪个小朋友会得到这份礼品呢?

样例输入

1 10

8 5

6 6

样例输出

1

3

4

模拟环形链表


既然题目中有一个数字圆圈,很容易想到用环形链表来模拟这个圆圈。我们可以创建一个有n个结点的环形链表,然后每次在这个链表中删除第m个结点。

可以使用STL中的list或者vector来模拟一个环形链表。t本身不是环形结构,所以每次当迭代器扫描到链表的末尾时,我们要将迭代器移到链表头。

代码如下:

#include <iostream>
#include <vector>
#include <iterator>using namespace std;//  调试开关
#define __tmain main#ifdef __tmain#define debug cout#else#define debug 0 && cout#endif // __tmainclass Solution
{
public:int LastRemaining_Solution(unsigned int n, unsigned int m){if(n < 1 || m < 1){return -1;}vector<int> numbers;for(int i = 0; i < n; i++){numbers.push_back(i);debug <<i <<" ";}debug <<endl;unsigned int cntN = 0;unsigned int cntM = -1;while(numbers.size( ) > 1){debug <<"count = " <<1 <<", num[" <<cntN <<"] = " <<numbers[cntN] <<endl;for(int cntM = 1; cntM < m; cntM++){++cntN;cntN %= numbers.size( );debug <<"count = " <<cntN + 1 <<", num[" <<cntN <<"] = " <<numbers[cntN] <<endl;}debug <<numbers[cntN] <<" leave the list" <<endl;numbers.erase(numbers.begin( ) + cntN);#ifdef debugcopy(numbers.begin( ),numbers.end( ),ostream_iterator< int >(cout, " "));debug <<endl;
#endif // debug}//  循环结束的时候, cntN = 1debug <<"count = " <<1 <<", num[" <<cntN <<"] = " <<numbers[cntN] <<endl;return numbers[0];}
};int __tmain( )
{Solution solu;cout <<solu.LastRemaining_Solution(5, 2) <<endl;//cout <<solu.LastRemaining_Solution(8, 5) <<endl;;//cout <<solu.LastRemaining_Solution(6, 6) <<endl;;return 0;
}

递推公式-获取最后的胜利者的序号


无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm)O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:
我们知道第一个人(编号一定是(m-1)) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m mod n的人开始):

k k+1 k+2 … n-2,n-1,0,1,2,… k-2

并且从k开始报0。
我们把他们的编号做一下转换:

k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k) mod n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 —- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式

让f[i]为i个人玩游戏报m退出最后的胜利者的编号,最后的结果自然是f[n]

f[1] = 0;

f[i] = (f[i - 1] + m) mod i;

有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。

注意我们的编号是从0开始,如果从1开始可以返回f[n] + 1

class Solution
{
public:int LastRemaining_Solution(unsigned int n, unsigned int m){if(n < 1 || m < 1){return -1;}int last = 0;debug <<"F[" <<0 <<"] = " <<last <<endl;for(int step = 2; step <= n; step++){last = (last + m) % step;debug <<"F[" <<step - 1 <<"] = " <<last <<endl;}return last;}
};

当然也可以使用递归方式

class Solution
{
public:int LastRemaining_Solution(unsigned int n, unsigned int m){if(n < 1 || m < 1){return -1;}else if(n == 1){return 0;}else{// F[n] = (F[n - 1] + m) % nreturn (LastRemaining_Solution(n-1,m)+m)%n;}}
};

剑指Offer--045-孩子们的游戏(圆圈中最后剩下的数)--约瑟夫环相关推荐

  1. 剑指offer:孩子们的游戏(圆圈中最后剩下的数)

    题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...

  2. 孩子们的游戏(圆圈中最后剩下的数)

    题目:孩子们的游戏(圆圈中最后剩下的数) 题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首 ...

  3. 《剑指Offer》62:圆圈中最后剩下的数字(约瑟夫环)

    题目 0,1,2-,n-1这n个数字排成一个圆圈,从数字0开始,每次从这圆圈你删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第 ...

  4. 剑指Offer - 面试题62. 圆圈中最后剩下的数字(约瑟夫环 递推公式)

    1. 题目 0,1,-,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次 ...

  5. 剑指offer 面试题62. 圆圈中最后剩下的数字

    0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字 ...

  6. 【剑指offer】62、圆圈中最后剩下的数字

    题目 0~n-1这n个数字排成一个圆圈,从0开始,每次删除第m个数字,求出圆圈里剩下的最后一个数字 思路 直接用数组模拟圆圈,模拟删除的过程 class Solution { public:int L ...

  7. 剑指offer第62题 圆圈中最后剩下的数字(约瑟夫问题)

    文章目录 问题描述: 解题思路: 代码实现: 问题描述: 0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3. ...

  8. java输出带圆圈数字_java实现孩子们的游戏(圆圈中最后剩下的数)

    题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...

  9. java名侦探柯南游戏大全_孩子们的游戏(圆圈中最后剩下的数),Java代码实现思路分享...

    下面给大家分享的是一个Java代码实例,下面一起来了解一下吧. 抽象建模能力 题目: 让小朋友们围成一个大圈,之后,随机指定一个数m,让编号为0的小朋友开始报数. 每一次,喊到了m-1的那个小朋友要出 ...

  10. 46. 孩子们的游戏-圆圈中最后剩下的数字

    题目描述: 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机 ...

最新文章

  1. CSS实现背景透明而背景上的文字图片不透明
  2. 业务专题篇:如何用数据评价某次活动?
  3. Intellij IDEA设置忽略部分类编译错误
  4. 为什么我开发的SAP UI5应用里的group by 功能不工作?
  5. react leaflet_如何使用Leaflet在React中轻松构建地图应用
  6. ps图案叠加如何导入?Photoshop添加图案叠加的详细操作
  7. OpenCV参考手册之Mat类详解
  8. ES6-使用let关键字定义变量
  9. 潘建伟团队再次刷新世界纪录:实现18个光量子比特纠缠
  10. sonar jacoco 覆盖率为0_接口测试代码覆盖率(jacoco)方案分享
  11. 电销机器人源码+图文介绍
  12. Python处理QXDM抓取log
  13. it职位简称_IT行业常见职位英文缩写
  14. 助睡眠的神器,这些东西能改善失眠,一觉睡到天亮
  15. android百度输入法切换,百度输入法输入模式怎么切换 快速一键切换输入法模式方法教程...
  16. scikit-learn:4.3. Preprocessing data(standardi/normali/binari..zation、encoding、missing value)
  17. PS2021 入门使用技巧
  18. 「云」上风景虽好,但不要盲目跟风
  19. 华为自研发操作系统鸿蒙,你知华为芯片“麒麟”之意,又可知自研系统“鸿蒙”之深意?...
  20. Chaquopy中不能导入64位Python训练的机器学习模型(32bit与64bit不兼容)

热门文章

  1. Unity3D-敌人巡逻和拉脱
  2. Vue入门---实现汇率换算
  3. 我们都应该停止三种测试实践
  4. mysql 存正负数_mysql 存储负数
  5. iptable端口重定向 MASQUERADE
  6. 计算机热启动方法,关于电脑热启动的介绍
  7. jike1901666
  8. 哈工大遗传学B期末复习
  9. 2021道路运输企业主要负责人安全考核试题
  10. Linux SSH服务启动,如何打开22端口?如何开启ssh远程链接