
本文共 2064 字,大约阅读时间需要 6 分钟。
题1:圆圈中最后剩下的数
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。
HF作为牛客的资深元老,自然也准备了一些小游戏。
其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
示例
输入
5,3返回值
3
解析:
-
从故事中抽取题目,可整理得:
给定一个由[0…n-1]构成的数组,第一次从0开始数m个数,然后删除,以后每次都从删除的数下一个位置开始数m个数,然后删除,直到剩余一个数字,找出那个数字。
如图:
在图中我们可以看到,在首次删除掉了(m-1)位元素后,之后都是不断在数组中***循环***遍历第m个数字进行删除。
-
对于循环的操作,此时可以用到
% (取余)运算
-
对于
%运算
的理解:-
3%10 = 1;
(3/10=3......1)
举例说明:
在数组[1,2,3],从第一位元素开始从左往右循环数10次,第10次必定会落在第一位。
-
数据结构中的
循环队列
也是运用这一操作(等下题刷到了再仔细讲解)
-
-
综上对于本次的操作,我们可以借用链表与%运算求出(见解答一)
-
不过答案提交之后显示运行时间位30+ms,总感觉太慢了需要优化。
-
-
查阅资料得知此题与 十分相似:
-
约瑟夫问题(有时也称为约瑟夫斯置换,是一个和中的问题。在计算机的中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)
-
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
-
最后可根据约瑟夫问题得出一个公式
|—— 0 n=1f(n,m)=| |—— [f(n-1,m)+m]%n n>1
-
具体推导可见博客:
首先分析可以得知,这是一道约瑟夫环的问题。具体约瑟夫环原理可以参考下面链接。
约瑟夫环——公式法(递推公式)讲解非常详细,必看
-
解答:
//借用链表:public int LastRemaining_Solution_withLinkedList(int n, int m) { LinkedListlist = new LinkedList (); for (int i = 0; i < n; i ++) { list.add(i); } int bt = 0; while (list.size() > 1) { bt = (bt + m - 1) % list.size(); //int num = list.get(bt); list.remove(bt); //System.out.println("移除:"+num+"---->"+list); } return list.size() == 1 ? list.get(0) : -1;}/*打印结果:与推理一致[0, 1, 2, 3, 4, 5]移除:2---->[0, 1, 3, 4, 5]移除:5---->[0, 1, 3, 4]移除:3---->[0, 1, 4]移除:1---->[0, 4]移除:4---->[0]*/
//运用约瑟夫环问题推导公式: public int LastRemaining_Solution(int n, int m) { if (n <= 0) return -1; int index = 0; for (int i=2; i<=n; ++i) { index = (index + m) % i; } return index; }