1 题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) 如果没有小朋友,请返回-1。
2 思路和方法
思路一:
考虑用环形链表解决;用stl中的list来模拟环形链表。list本身不是环形结构,所以每当迭代器扫描到链表末尾时,需要把迭代器移到链表的头部,list的erase方法删除节点后,原来的迭代器会失效,所以需要保存下一个节点。或者使用vector模拟环形链表。
思路二:
https://blog.csdn.net/SCS199411/article/details/92988332
分析每次被删除数字的规律直接计算出圆圈中最后剩下的数字。隐函数f(n,m)为在n个数字中每次删除第m个数字最后剩下的数字。
相关知识点:
取余运算的一些性质需要熟悉:一、某数取余多次与取一次相等,例如5%2=1,而5%2%2%2=1;二、取余运算满足结合律,例如a%n - 1=(a-1)%n (n > 1);三、如果对于一个明显不在[0, n-1]区间的数x,在该区间的同余数有且只有1个。例如x = -2,-2%n=n-2。
3 C++核心代码

1 class Solution { 2 public: 3 int LastRemaining_Solution(int n, int m) 4 { 5 // 环形链表 6 // STL中的list只有没有循坏,每次访问到链表末尾时要重新指向链表头 7 int LastRemaining_Solution(int n, int m){ 8 if (n<1 || m<1) 9 return 0; 10 11 list<int> nums; 12 for (int i = 0; i < n; ++i) { 13 nums.push_back(i); 14 } 15 16 list<int>::iterator cur = nums.begin(); 17 while (nums.size()>1){ 18 for (int i = 1; i < m; ++i) { 19 cur++; 20 if(cur == nums.end()) 21 cur = nums.begin(); 22 } 23 list<int>::iterator next = ++cur; // 保存下一个节点 24 if (next == nums.end()) 25 next = nums.begin(); 26 cur--; 27 nums.erase(cur); // 删除该节点 28 cur = next; // 指向下一个节点 29 } 30 return *(cur); 31 } 32 };

1 class Solution { 2 public: 3 int LastRemaining_Solution(int n, int m) 4 { 5 if(n < 1 || m < 1) //守卫代码 6 return -1; 7 8 //int last = 0; 9 //for (int i = 2; i<=n; ++i){ 10 // last = (last+ m) % i; 11 //} 12 //return last; 13 14 if(n == 1) 15 return 0; 16 return (LastRemaining_Solution(n-1, m) + m) % n; 17 } 18 };
4 C++完整代码

1 #include <iostream> 2 #include <vector> 3 #include <list> 4 5 using namespace std; 6 7 int LastRemaining_Solution(int n, int m); 8 9 int main() { 10 cout << LastRemaining_Solution(5, 3) << endl; 11 12 system("pause"); 13 return 0; 14 } 15 16 int LastRemaining_Solution(int n, int m) { 17 if (n < 1 || m < 1)return -1; 18 19 //使用vector来模拟一个环形链表 20 vector<int> num; 21 for (int i = 0; i != n; i++) { 22 num.push_back(i); 23 } 24 25 int start = 0; 26 while (num.size() > 1) { 27 int cntDown = m; 28 int mv = start; 29 while (--cntDown) { 30 //如果遍历到了最后一个数字,下一个遍历数组首个数字 31 if (mv == (num.size() - 1)) { 32 mv = 0; 33 } 34 else { 35 mv++; 36 } 37 } 38 39 if (mv == (num.size() - 1)) { 40 start = 0; 41 } 42 else { 43 start = mv; 44 } 45 num.erase(num.begin()+mv); 46 } 47 48 return num[0]; 49 }
参考资料
https://blog.csdn.net/zjwreal/article/details/88917056
https://blog.csdn.net/m0_37950361/article/details/82154753
https://blog.csdn.net/SCS199411/article/details/92988332