题目link:https://www.luogu.com.cn/problem/UVA1394


Part0:

题意简化:

约瑟夫问题。

给定 $n$ 个编号由 $1$ $\text{~}$ $n$ 的人,最开始杀掉第 $m$ 个人,接着每数 $k$ 个人就杀掉最后数到的那个,求最后剩下的人的编号。


Part1:

首先容易得出一个 $O(n^2)$ 的用数组循环暴力模拟的算法,但是发现这个算法会枚举到已经死掉的人,因此考虑用链表优化。


Part2:

链表可以 $O(1)$ 进行删除,因此可以 $O(nk)$ 地解决这个问题,但是显然 $n$,$k$ 最大为 $10^4$,$O(nk)$ 过不了此题,所以考虑用递推解决。

这里也给出链表做法的代码:

 1 #include <cstdio>
 2 
 3 const int MAXN = 1e4;
 4 
 5 int n, k, m;
 6 
 7 struct Node {
 8     int nxt, pre, val, dead;
 9 }lst[MAXN + 10];
10 
11 void remove(int id) {
12 
13     lst[id].dead = 1;
14     lst[lst[id].nxt].pre = lst[id].pre;
15     lst[lst[id].pre].nxt = lst[id].nxt;
16 
17     return;
18 }
19 
20 int main() {
21 
22     while(233) {
23 
24         scanf("%d %d %d", &n, &k, &m);
25         if(!n && !k && !m) return 0;
26 
27         for(int i = 1; i <= MAXN; ++i) {
28             lst[i].dead = 0, lst[i].nxt = 0, lst[i].pre = 0, lst[i].val = 0;
29         }
30         lst[1].nxt = 2, lst[1].pre = n, lst[1].val = 1;
31         lst[n].nxt = 1, lst[n].pre = n - 1, lst[n].val = n;
32         for(int i = 2; i < n; ++i) {
33             lst[i].pre = i - 1, lst[i].nxt = i + 1, lst[i].val = i;
34         }
35 
36         remove(m);
37         int numKill = 1;
38         int lastKill = m;
39         while(++numKill != n) {
40             for(int i = 1; i <= k; ++i) {
41                 lastKill = lst[lastKill].nxt;
42             }
43             remove(lastKill);
44         }
45 
46         for(int i = 1; i <= n; ++i) {
47             if(!lst[i].dead) {
48                 printf("%d\n", i);
49                 break;
50             }
51         }
52 
53     }
54 
55     return 0;
56 }
链表代码

相关文章: