题目:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常,我们会要求输出最后一位出列的人的序号。那么这里主要研究的是最后一个出列的人的序号要怎么确定。
首先,数组写成以0开头更便于计算,所以后面的分析都是以0开头的。
原理(缺)
https://blog.csdn.net/byn12345/article/details/79487253
https://www.cnblogs.com/cmmdc/p/7216726.html
1,借用模(取余),数组可以轻松表示环形。数组 [0, 1, 2, 3, 4, 5] 可以写成:[6%6, 7%6, 8%6, 3%6, 4%6, 5%6]. 我们把递增的起点从第0个元素移动到第3个元素处。即实现了3,4,5,0,1,2的单调递增。
2,把数组 [0,1,2,3,4,5,6,7,8,9] 滚动成 [4,5,6,7,8,9,0,1,2,3]的方法: 相当于是重新把6作为基准,其他数仍然照序递增(包括跨过数组右界回到数组左界,这个过程也是递增的),所以变化方法只需要给出6如何变成0即可: (k-6)%10 , 为了保证括号里大于零,所以额外加个模,即:(k-6+10)%10 ====> (k+4)%10.
解法一:用递归公式
每删除一个元素,就把新队列的下标映射成规范形式(即从0开始),这样已知最后一个取得元素其变换后的下标必然是m,则能逆向计算出变换前的下标。
int lastNum(int n, int m) {
int index = 0, len = 1; //len表示变换前的数组长度
while (++len <= n)
index = (index + m % len + 1) % len;
return index;
}
解法二:用队列模拟
用队列表示环形数组,并且模拟数数的过程。数一个数相当于从队首取出一个元素,若计数没到k,就把它放回队尾,若到了k,就不再放回(相当于删除掉了),并且重置计数。直到队列剩余1个元素。
#include<queue>
int lastNum(int n,int m) {
queue<int> q;
for (int i = 0; i<n; i++) q.push(i); //初始队列0~n-1
int count = 0;
while (q.size() != 1) {
if (count != m) { //未数到m
int tmp = q.front();
q.pop();
q.push(tmp);
count++;
}
else { //数到m
q.pop();
count = 0;
}
}
int res = q.front();
return res;
}