题目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 }