方法1: 用环形单链表来模拟圆圈
可转换为带环单链表删除结点的问题:创建一个总共有n个结点的环形链表,然后每次在这个链表中删除第m个结点。
时间复杂度为O(n*m),空间复杂度为O(n).
方法2:映射,找规律
时间复杂度为O(n),空间复杂度为O(1).
此题只需求最后一个报数胜利者,我们可以用数学归纳法解决该问题,为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
分析:
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%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
k-1 --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解: 例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,不难推导出:x'=(x+k)%n。
令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]。
相应的递推关系为:
注意:此题中m是不变的量,从刚开始输入后就一直不变,而n逐一减小...
方法2
递归实现 AC代码:
class Solution { public: int LastRemaining_Solution(int n, int m) { if(n<1 || m<1) return -1; if(n==1) return 0; return (LastRemaining_Solution(n-1,m)+m)%n; } };
或
class Solution { public: int LastRemaining_Solution(unsigned int n, unsigned int m) { if(n==0 || m==0) return -1; if(n==1) return 0; return (LastRemaining_Solution(n-1,m)+m)%n; } };
class Solution { public: int LastRemaining_Solution(unsigned int n, unsigned int m) { if(n==0 || m==0) return -1; if(n==1) return 0; int last=0; // f(1,x)=0 for(unsigned int i=2; i<=n; i++) { last=(last+m)%i; } return last; } };
ZOJ上也有相关的题:
ZOJ Problem Set - 1088
提交网址: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=88
Time Limit: 10 Seconds Memory Limit: 32768 KB
Recently you must have experienced that when too many people use the BBS simultaneously, the net becomes very, very slow.
To put an end to this problem, the Sysop has developed a contingency scheme for times of peak load to cut off net access for some buildings of the university in a systematic, totally fair manner. Our university buildings were enumerated randomly from 1 to n. XWB is number 1, CaoGuangBiao (CGB) Building is number 2, and so on in a purely random order.
Then a number m would be picked at random, and BBS access would first be cut off in building 1 (clearly the fairest starting point) and then in every mth building after that, wrapping around to 1 after n, and ignoring buildings already cut off. For example, if n=17 and m=5, net access would be cut off to the buildings in the order [1,6,11,16,5,12,2,9,17,10,4,15,14,3,8,13,7]. The problem is that it is clearly fairest to cut off CGB Building last (after all, this is where the best programmers come from), so for a given n, the random number m needs to be carefully chosen so that building 2 is the last building selected.
Your job is to write a program that will read in a number of buildings n and then determine the smallest integer m that will ensure that our CGB Building can surf the net while the rest of the university is cut off.
3 4 5 6 7 8 9 10 11 12 0
2 5 2 4 3 11 2 3 8 16
AC代码(PHP实现):
<?php function judge($m, $n) { $from = 0; for($i = 2; $i < $n; $i++) $from = ($from + $m)%($i); if($from + 1 == 1) return 1; else return 0; } $fd = STDIN; // STDIN是oj提供的常量,等价于 fopen("php://stdin","r")); while(fscanf($fd, "%d", $n)==1 && $n!=0) { $m = 1; while(!judge($m, $n)) { ++$m; } echo $m."\n"; }
带测试的PHP代码,请参考本人博文:详解OJ(Online Judge)中PHP代码的提交方法及要点【举例:ZOJ 1001 (A + B Problem)】 的末尾部分...
相关链接:
http://www.cnblogs.com/AndyJee/p/4687715.html