P1996. 约瑟夫问题

考点

  • 链表
  • 队列
  • 模拟

题解

见思路

思路

这道题无法使用数组,因为要删除元素...

双向链表

用链表实现很简单,每次走m - 1步,输出对应元素并删除;期间如果走到了链表末尾,就从伪节点的后继节点继续遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;
const int LEN = 200;

struct Node
{
int pre_, nxt_, key_;
Node(int pre = 0, int nxt = 0, int key = 0) : pre_(pre), nxt_(nxt), key_(key) {}
} arr[LEN];

//伪节点
#define dummyhead arr[0]
int tot = 0;

//删除now下标的节点
void del(int now)
{
arr[arr[now].pre_].nxt_ = arr[now].nxt_;
arr[arr[now].nxt_].pre_ = arr[now].pre_;
}

int main()
{
int n, m;
cin >> n >> m;
//新建链表节点
for (int i = 1; i <= n; ++i)
{
arr[++tot] = Node(tot, arr[tot].nxt_, i);
arr[tot - 1].nxt_ = tot;
}
//下标0是伪节点,应从下标1开始遍历
int now = 1;
while (dummyhead.nxt_)
{
for (int i = 1; i < m; ++i)
{
now = arr[now].nxt_;
if (!now)
now = dummyhead.nxt_;
}
int nxt = arr[now].nxt_;
if (!nxt)
nxt = dummyhead.nxt_;
cout << arr[now].key_ << " ";
del(now);
now = nxt;
}
return 0;
}

队列

如果把队首的元素移动到队尾,不就相当于循环访问吗?

每次队首的人出队,然后移到队尾,一直循环直到第k个人,将这个人删除即可,重复这一操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;

int main()
{
int n, m;
cin >> n >> m;
queue<int> q;
for (int i = 1; i <= n; ++i)
q.emplace(i);
while (!q.empty())
{
for (int cnt = 1; cnt < m; ++cnt)
{
q.emplace(q.front());
q.pop();
}
cout << q.front() << " ";
q.pop();
}
return 0;
}