一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?
- 输入格式:输入在一行中给一个正整数N(≤1000)。
- 输出格式:在一行中输出当选猴王的编号。
输入样例:
11
输出样例:
7
思路:最简单的一类约瑟夫问题,这里总是每次隔着三个。模拟示例:
1 2 3 4 5 6 7 8 9 10 11 -> 3
1 2 4 5 7 6 8 9 10 11 -> 6
1 2 4 5 7 8 9 10 11 -> 9
1 2 4 5 7 8 10 11 -> 1
2 4 5 7 8 10 11 -> 5
2 4 7 8 10 11 -> 10
2 4 7 8 11 -> 4
2 7 8 11 -> 11
2 7 8 -> 8
2 7 -> 2
7
- 方法一:静态数组模拟全过程,进行实际的删除。最后剩下的就是答案。
#include <iostream> using namespace std; int n; void dele(int *a, int t) { if (t < 0 || t >= n) return; for (int i = t; i < n - 1; ++i) a[i] = a[i + 1]; } //void print(int *a) { // for (int i = 0; i < n; ++i) // printf("%d ", a[i]); //} int main() { cin >> n; int arr[n]; for (int i = 0; i < n; ++i) arr[i] = i + 1; int pos = 0; while (n > 1) { pos = (pos + 2) % n; // print(arr); // printf(" | a[%d] = %d\n", pos, arr[pos]); dele(arr, pos); n--; } cout << arr[0]; return 0; }
- 方法二:用vector,简化过程,进行实际的删除。当然,无论是方法一还是方法二,都有实际的模拟和删除的过程,而且删除的过程都需要线性时间,这样频繁移动元素的效率很低。
#include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { int n; cin >> n; vector<int> arr(n); for (int i = 0; i < arr.size(); ++i) arr[i] = i + 1; int pos = 0; // 要删除的下标 while (arr.size() > 1) { pos = (pos + 2) % arr.size(); arr.erase(arr.begin() + pos); } cout << arr[0]; return 0; }
- 方法三:使用list,它是双向链表,可以在任意地方高效率的插入和删除,不支持[ ]和随机访问,用iterator来进行这些操作。push_front/push_back/pop_front/pop_back,insert/erase。当然,也可以手动模拟链表。
#include <iostream> #include <list> using namespace std; int main() { list<int> a; int n; cin >> n; for (int i = 0; i < n; ++i) a.push_back(i + 1); list<int>::iterator it; // 通过迭代器删除 int kth = 1; // 数数 while (a.size() > 1) { for (it = a.begin(); it != a.end(); ) { if (kth++ % 3 == 0) it = a.erase(it); // 删除这个元素, 并返回下一个元素的迭代器 else ++it; // 不用删除的时候, 直接指向下一个元素 } } cout << *(a.begin()); return 0; }
- 方法四:静态数组,不删除元素,置0,因此必须数数而不能直接计算pos值,每次从非零的元素数起,数到了就置相应位置为0。最后剩下一个非零元素。
#include <iostream> #include <list> using namespace std; int main() { int n; cin >> n; int a[n]; for (int i = 0; i < n; ++i) a[i] = i + 1; int kth = 1, cnt = n; // kth为第几个, cnt确定数组实际大小 while (cnt > 1) { for (int i = 0; i < n; ++i) { // 没有删除元素, n不变 if (a[i] != 0 && kth++ % 3 == 0) { // 为实际元素且应该删除 a[i] = 0; --cnt; } } } int i = 0; while (i < n && a[i] == 0) ++i; cout << a[i]; return 0; }
- 方法五:
#include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { int n; cin >> n; int p = 0; for (int i = 2; i <= n; ++i) { p = (p + 3) % i; } cout << p + 1; return 0; }