题目描述
共有 n 名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1 到 n 编号。确切地说,从第 i 名小伙伴顺时针移动一位会到达第 (i+1) 名小伙伴的位置,其中 1 <= i < n ,从第 n 名小伙伴顺时针移动一位会回到第 1 名小伙伴的位置。
游戏遵循如下规则:
从第 1 名小伙伴所在位置 开始 。
沿着顺时针方向数 k 名小伙伴,计数时需要 包含 起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。
你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤 2 继续执行。
否则,圈子中最后一名小伙伴赢得游戏。
给你参与游戏的小伙伴总数 n ,和一个整数 k ,返回游戏的获胜者。
1 <= k <= n <= 500
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-winner-of-the-circular-game
具体示例请看官网。
题目思考
对于本问题,无非是在环形圈中按照一定的模式排除小伙伴,最后只剩下一个人。我们首先应该用数学的思想去思考这个问题,显而易见,对于一组n,k只会对应唯一一个小伙伴。我们先称这个小伙伴为m,则可以将上述内容用公式表为:f(n,k) = m,其中f代表一个函数或者一个模式,使得n,k能映射到正确的m。我们接下来的任务就是去思考或者总结归纳出这个f。那么这个f到底是怎样的一个函数或者模式呢?
1.我们先考察一组n,k所有的可能就是k个。
2.我们现设n=2,k=1,不难发现m=2,我们再设n=3,k=1也不难发现m=3,以此类推,当n=u,k=1时,m=u。那么当k=2时会是怎样的情况呢?答案是变得不容易发现规律。
3.设n=2,k=2,则m=1,当n=3,k=2时,m=2,当n=4,k=2时,m=1,当n=5, k=2时,m=3......
4.通过观察,我们还是很难发现这个n,k与m之间存在的这个f是什么,但是不难看出,f是单一函数的这个可能就可以排除了。可究竟是什么呢?这个答案就是,如果你没有相当强的数感,很难得出这个f究竟是什么,既然这样,我们应该尝试着换种思路。
5.我们试着将第一轮与第二轮,第二轮与第三轮等等的序列总结出一个映射关系,然后从中发现最后一轮剩下数的序列与上一轮的序列比较,然后通过这个映射公式与剩下的这个数建立关系式,详细的可以参考这篇文章,https://zhuanlan.zhihu.com/p/121159246,最终得出递推公式:f(n,k) = (f(n-1,k) + k) % i,其中f(1,k) = 0, 1<=i<=n;
6.虽然递归式能很好利用计算机解决问题,但这还不是最完美的,至今为止还没有证明出这个问题不能用一个特定函数来解决,也就是说这个问题有可能存在一个特定的函数,只是人们还没发现。当然笔者也在思考这个问题,如果存在一个特定函数的话,那也是一个不平常的函数,可能是一个积分式,可能会用到更高深的数学理论,但这想必是个很有趣的问题。
我们来看两个不同版本的代码吧,第一种是常规思路,第二种则是用递归方法,笔者一直在想第三种用特定函数解决,不过我想这可能有解,可能没解,这至少还没有谁能证明出来,我就暂且相信有吧。
第一种:
var findTheWinner = function(n, k) {
var map = new Array(n+1);
map.fill(0,0,n+2);
var count = 0;
var cur = 0;
while(count < n-1)
{
let skip = 0;
while(skip < k)
{
if(cur >= n)
{
cur = 0;
}
if(map[cur+1] == 0)
{
skip++;
cur++;
}else{
cur++;
}
}
if(map[cur] == 0){
map[cur] = 1;
count++;
}
}
return map.indexOf(0,1);
};
第二种:
var findTheWinner = function(n, k) {
var f = 0;
for(let i = 1; i <= n; i++) {
f = (f+k) % i;
}
return f+1;
}