一羣猴子要選新猴王。新猴王的選擇方法是:讓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; }