[洛谷]P1996 约瑟夫问题
- 一、问题描述
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 提示
- 二、思路分析
- 1、算法标签:
- 2、算法分析:
- 三、代码实现
- 1、环形链表
- 2、队列
一、问题描述
[洛谷]P1996 约瑟夫问题
题目描述
n n n 个人围成一圈,从第一个人开始报数,数到 m m m 的人出列,再由下一个人重新从 1 1 1 开始报数,数到 m m m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入格式
输入两个整数 n , m n,m n,m。
输出格式
输出一行 n n n 个整数,按顺序输出每个出圈人的编号。
样例 #1
样例输入 #1
10 3
样例输出 #1
3 6 9 2 7 1 8 5 10 4
提示
1 ≤ m , n ≤ 100 1 \le m, n \le 100 1≤m,n≤100
二、思路分析
1、算法标签:
这道题考察的是数据结构中的:环形链表、队列
2、算法分析:
这道题的关键其实在于如何将进行循环访问。
我们最容易想到的其实就是利用数组模拟一个环形链表。这样的话,每当我们遍历到尾部节点的时候,就会自动跳到头节点。
当然我们还可以使用一个队列。每次报数的时候,我们就让队头出队。如果这个人不是要出局的人,我们就让这个人再次入队,等待下一次报数。如果这个人是要出局的人,那么这个人就不需要再入队了,直接淘汰即可。
三、代码实现
1、环形链表
#include<iostream>
using namespace std;
const int N=110;
int e[N],ne[N],idx,h=-1;
int n,m;
void add(int x)
{
e[idx]=x,ne[idx]=h,h=idx++;
}
void del(int x)
{
ne[x]=ne[ne[x]];
}
int main()
{
cin>>n>>m;
for(int i=n;i>=1;i--)
add(i);
//构造环形队列,让队尾指向队头
ne[0]=h;
int k=0;
int prev=0;
int num=0;
for(int i=h;num!=n;i=ne[i])
{
k++;
if(k==m)
{
del(prev);
num++;
cout<<e[i]<<" ";
k=0;
}
prev=i;
}
return 0;
}
由于我们是头插的,所以我们最开始插入的那个节点最终会编程尾部节点,即我们 i d x = 0 idx=0 idx=0时对应的那个节点就是我们最终的尾部节点。
我们想要构造一个环形链表的话,就需要让尾部节点记录头指针。
所以才出现了我们的这一行代码:ne[0]=h;
2、队列
#include<iostream>
#include<queue>
using namespace std;
queue<int>q;
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
q.push(i);
int k=0;
while(!q.empty())
{
int t=q.front();
k++;
q.pop();
if(k!=m)
{
q.push(t);
}
else
{
k=0;
cout<<t<<" ";
}
}
return 0;
}