100 ≤ m,n ≤ 100
【题解】
问题其实并不困难,但是目的就是利用题目来锻炼自己的数据结构。
给出一题五解的做法。
【解法一】
利用STL里面的list,注意指针到了链表尾部要指回链表的头部。
#include
using namespace std;
typedef long long ll;
int n,m;
int main()
{
list List ;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) List.push_back(i);
list ::iterator it = List.begin(),tmp;
for(int i=;i<=n;i++){
for(int j=;j
it++;
if( it == List.end() )
it = List.begin() ;
}
printf("%d ",*it);
tmp = it ;
it++;
if( it == List.end() ) it = List.begin() ;
List.erase(tmp);
}
return ;
}
STL——list
【解法二】
利用STL里面的queue,实现该过程,从队头出来,从队尾插入。
#include
#include
using namespace std;
int main()
{
int n,m;
queue Q;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
Q.push(i);
}
for(int i=;i<=n;i++){
int cur = Q.front();
Q.pop();
for(int j=;j
Q.push(cur);
cur = Q.front() ;
Q.pop();
}
printf("%d%c",cur,i==n?'\n':' ');
}
return ;
}
STL—queue
【解法三】
手工实现链表
#include
#include
#include
using namespace std;
const int N = 1e5+;
typedef struct Node{
int val ;
Node * next;
}Node;
Node *head , *tail , *tmp, *p ;
int main()
{
int n,m;
head = new Node ;
head -> next = NULL ;
tail = head ;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
p = new Node ;
p -> val = i ;
p -> next = NULL ;
tail -> next = p ;
tail = p ;
}
p = head -> next ;
tail -> next = head -> next;
for(int i=;i<=n;i++){
for(int j=;j
p = p->next;
}
printf("%d ",p->next->val);
tmp = p -> next ;
p -> next = tmp -> next ;
p = p -> next ;
free(tmp);
}
return ;
}
手工链表
【解法四】
手工实现队列
#include
#include
using namespace std;
const int N = 1e5+;
int main()
{
int n,m;
int Head = , Tail = ;
int Q[N];
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
Q[++Tail] = i ;
}
for(int i=;i<=n;i++){
int cur = Q[Head++];
for(int j=;j
Q[++Tail] = cur ;
cur = Q[Head++];
}
printf("%d%c",cur,i==n?'\n':' ');
}
return ;
}
手工队列
【解法五】
这个题目最正解的做法是权值线段树,权值树状数组。
#include
#include
using namespace std;
const int N=;
int n,m;
struct Stree
{
int l,r;
int dat;
}t[N<
//结构体
//建树
void build(int p,int l,int r)
{
t[p].l=l;t[p].r=r;
if(l==r)
{
t[p].dat=;
//初始化为1,表示这里是有人的
return;
}
int mid=(l+r)>>;
build(p<
build(p<
t[p].dat=t[p<
}
//把 x 踢出去
void change(int p,int x)
{
if(t[p].l==t[p].r)
{
t[p].dat=;
return;
}
int mid=(t[p].l+t[p].r)>>;
if(x<=mid) change(p<
else change(p<
t[p].dat=t[p<
}
//查询 x 的位置
int query(int p,int x)
{
if(t[p].l==t[p].r)
return t[p].l;
//如果左边的剩余位置小于这个编号,那就在右边区域查找左边区域放不下的
if(x>t[p<
else return query(p<
}
int main()
{
scanf("%d%d",&n,&m);
if(n==) return ;
build(,,n);
int pos=;
while(n)
{
pos=(pos+m-)%t[].dat+;//t[1].dat即剩余总人数
//先给 pos-1, 避免出现mod 完变成0的情况,mod完之后在 +1
//处理位置
// if(pos==0) pos=t[1].dat;
int qwq=query(,pos);
//查寻当前这个人的位置
cout<
//输出
change(,qwq);
//踢出队伍
n--;
}
return ;
}
//By Yfengzi
权值线段树
#include
#include
using namespace std;
const int maxn=3e4+;
int n,m,maxx;
int bit[maxn];
inline int lowbit(int x)
{
return x&-x;
}
inline void add(int pos,int x)
{
for(int i=pos;i<=maxx;i+=lowbit(i))bit[i]+=x;
}
inline int find_kth(int k)
{
int ans=,now=;
for(int i=;i>=;i--)
{
ans+=(<
if(ans>maxx||bit[ans]+now>=k)ans-=(<
else now+=bit[ans];
}
return ans+;
}
int main()
{
scanf("%d %d",&n,&m);
maxx=n; //这里因为n后面会改变,所以先记录一下n的值。
for(int i=;i<=n;i++)bit[i]=lowbit(i);//这里完全等价于add(i,1),因为一开始都是1,所以bit[i]=i-(i-lowbit(i)+1)+1=lowbit(i)
int now=;//从1开始
while(n)
{
now=(now-+m-)%n+;//这里是小细节,本来的式子应该是(now+m-1)%n的,但是考虑如果只剩下2个元素,而我们当前要找的就是第二个元素呢?直接模就是0了,所以用一个+1 -1 的小操作更改取模运算的值域,这样就可以取到n的值了,而对别的无影响
int ans=find_kth(now);//找kth
add(ans,-);//把这个人删除
printf("%d ",ans);
n--;
}
return ;
}
权值树状数组