题目
给定人数,从1-2报数,报到2的人出列,再从1-3报数,报到3的人出列,如此循环往复,知道队伍中的总人数小于三。
数据结构
使用双向链表存放,满足双向链表的特点:
- 插入删除操作频繁
- 随机访问较少
算法
当List.size()>3
时继续循环,使用flag
来标记当前报数种类。
停止循环后输出。
每次删除前先计算一共会进行多少次删除操作
如果flag=2
的话,每进行一次删除操作(假设现在删除的是第i
位),后一位就会前进一位来填补空缺,我们下一位要删除的便是i+1
位。
同理可得,当flag=3
时,我们下一位要删除的应该是第i+2
位。
而退出每一次报数的条件就是当i>l.size()
时,退出循环。
细节
这里num++
一直在走,但是it++
在%flag=0
时不走,这样就形成一个类似补位的机制,因为list内有元素被释放掉了,后面的元素会向前补一位,这样相当于it
已经向前走了一步,所以当满足%flag=0
的条件时,num
依然++
,而lt
却不++
。
代码
#include<iostream>
#include<List>
using namespace std;
int main()
{
int T; cin >> T;
while(T--){
int n; cin >> n;
list<int>l;
for(int i=1;i<=n;i++){
l.push_back(i);
}
int flag=2;
while(l.size()>3){
int num=1;//这里num一定要放在这里定义,因为每一次循环结束,num就需要从1开始
for(list<int> ::iterator it=l.begin();it!=l.end();){//这里num++一直在走,但是it++在%flag=0时不走,这样就形成一个类似补位的机制,因为list内有元素被释放掉了,后面的元素会向前补一位,这样相当于it已经向前走了一步,所以当满足%flag=0的条件时,num依然++,而lt却不++
if( num++ %flag==0){
it=l.erase(it);
}else{
it++;
}
}
flag==2?flag=3:flag=2;
}
cout << l.front(); l.pop_front();
while(!l.empty()){
cout << " ";
cout << l.front(); l.pop_front();
}
cout << endl;
}
system("pause");
return 0;
}