代码提交:nowcode
其实是一个一步步从最后只剩一个人到还原第一次n个人编号的过程,而不是一步步把每次出局的人找出来,其实过程是未知的
n个小朋友们围成一个大圈,编号0…n-1
随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱歌,并且不再回到圈中;
从刚出列的下一个小朋友开始,继续0…m-1报数…
这样下去…
直到剩下最后一个小朋友,求最后一个小朋友的编号。
举例看规律n = 8, m = 3
出列 | 剩下 | |
---|---|---|
第1轮 | 2 | 0,1,–,3,4,5,6,7 |
第2轮 | 5 | 0,1,–,3,4,–,6,7 |
第3轮 | 0 | –,1,–,3,4,–,6,7 |
第4轮 | 4 | –,1,–,3,–,--,6,7 |
第5轮 | 1 | –,--,–,3,–,--,6,7 |
第6轮 | 7 | –,--,–,3,–,--,6,– |
第7轮 | 3 | –,--,–,--,–,--,6,– |
第8轮 | 6 | –,--,–,--,–,--,–,-- |
每次出列之后,重新编号,每次都会是新号中m-1个人出列。但要还原回原序列中的编号,一步一步向上返:
出列 | 剩下 | 备注(m=3) | ||
---|---|---|---|---|
f[1] | 第8轮 | 6 | , , ,, , ,0, | 最后只有一个人,编号肯定为0 |
f[2] | 第7轮 | 3 | , , ,0, , ,1, | 在2个人中没有被淘汰,编号肯定为m%2 |
f[3] | 第6轮 | 7 | , , ,0, , ,1,2 | 2人编号时为1,那这次编号为(1+m)%3 |
f[4] | 第5轮 | 1 | ,2, ,3, , ,0,1 | |
f[5] | 第4轮 | 4 | ,0, ,1,2, ,3,4 | |
f[6] | 第3轮 | 0 | 2,3, ,4,5, ,0,1 | |
f[7] | 第2轮 | 5 | 5,6, ,0,1,2,3,4 | |
f[8] | 第1轮 | 2 | 0,1,2,3,4,5,6,7 |
最后肯定是留下一个人,新编号f[1]是0,但他在上一次的编码(0,1)中f[2]是多少呢?
f[2] = (f[1] + 3)% 2 = 1
他在上上次的编码(0, 1,2)中f[3]是多少呢?
f[3] = (f[2] + 3)% 3 = 1
类似的,能够推出在原序列中的编码是f[n]
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n == 0)
return -1;
if(n == 1)
return 0;
int f[n+4];
f[0] = 0;
f[1] = 0;
for(int i = 2;i <= n;i++){
f[i] = (f[i-1] + m) % i;
}
return f[n];
}
};