目录
简介
有n个人(编号从1到n),围成一圈坐在一起。从第一个人开始报数,每报到m的人出列,然后从下一个人开始重新报数,直到最后剩下一个人为止。要求根据给定的n和m,确定最后剩下的人的编号。
约瑟夫问题有多种解法,包括使用循环链表、数组模拟、递归求解、数学公式等。其中使用队列实现的方法是比较常见的一个解法。这种方法利用队列的先进先出特性,不断将报数小于m的人重新入队,直到只剩下最后一个人。
解法一:循环链表法
#include <iostream>
using namespace std;
struct Node {
int value;
Node* next;
Node(int val) : value(val), next(nullptr) {}
};
int josephus(int n, int m) {
Node* head = new Node(1);
Node* prev = head;
for (int i = 2; i <= n; ++i) {
Node* newNode = new Node(i);
prev->next = newNode;
prev = newNode;
}
prev->next = head; // 循环链表
Node* curr = head;
while (curr->next != curr) {
int count = 1;
while (count != m-1) {
curr = curr->next;
++count;
}
Node* temp = curr->next;
curr->next = temp->next;
delete temp;
curr = curr->next;
}
int lastPerson = curr->value;
delete curr;
return lastPerson;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法二:数组模拟法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
bool* eliminated = new bool[n];
for (int i = 0; i < n; ++i) {
eliminated[i] = false;
}
int remaining = n; // 剩余的人数
int index = 0; // 当前报数的人的索引
while (remaining > 1) {
int count = 0; // 报数的次数
while (count < m) {
if (!eliminated[index]) {
++count;
if (count == m) {
eliminated[index] = true;
--remaining;
}
}
index = (index + 1) % n;
}
}
int lastPerson = 0;
for (int i = 0; i < n; ++i) {
if (!eliminated[i]) {
lastPerson = i + 1;
break;
}
}
delete[] eliminated;
return lastPerson;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法三:递归法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
if (n == 1) {
return 1;
} else {
return (josephus(n - 1, m) + m - 1) % n + 1;
}
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法四:数学公式法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
int lastPerson = 0;
for (int i = 2; i <= n; ++i) {
lastPerson = (lastPerson + m) % i;
}
return lastPerson + 1;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法五:动态规划法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
int dp = 0;
for (int i = 2; i <= n; ++i) {
dp = (dp + m) % i;
}
return dp + 1;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法六:二进制法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
int lastPerson = 0;
for (int i = 1; i <= n; ++i) {
lastPerson = (lastPerson + m) % i;
}
return lastPerson + 1;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法七:约瑟夫环公式法
#include <iostream>
#include <cmath>
using namespace std;
int josephus(int n, int m) {
if (n == 1) {
return 1;
} else {
return ((josephus(n - 1, m) + m - 1) % n) + 1;
}
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法八:迭代法
#include <iostream>
using namespace std;
int josephus(int n, int m) {
int lastPerson = 0;
for (int i = 2; i <= n; ++i) {
lastPerson = (lastPerson + m) % i;
}
return lastPerson + 1;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法九:动态数组模拟法
#include <iostream>
#include <vector>
using namespace std;
int josephus(int n, int m) {
vector<bool> eliminated(n, false);
int remaining = n; // 剩余的人数
int index = 0; // 当前报数的人的索引
while (remaining > 1) {
int count = 0; // 报数的次数
while (count < m) {
if (!eliminated[index]) {
++count;
if (count == m) {
eliminated[index] = true;
--remaining;
}
}
index = (index + 1) % n;
}
}
int lastPerson = 0;
for (int i = 0; i < n; ++i) {
if (!eliminated[i]) {
lastPerson = i + 1;
break;
}
}
return lastPerson;
}
int main() {
int n = 10; // 人数
int m = 3; // 报到m的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}
解法十:队列法
#include <iostream>
#include <queue>
using namespace std;
int josephus(int n, int m) {
queue<int> q;
for (int i = 1; i <= n; ++i) {
q.push(i);
}
while (q.size() > 1) {
for (int i = 1; i < m; ++i) {
q.push(q.front()); // 报数小于 m 的人重新入队
q.pop();
}
q.pop(); // 报数为 m 的人出队
}
return q.front();
}
int main() {
int n = 10; // 总人数
int m = 3; // 报数到 m 的人出列
int lastPerson = josephus(n, m);
cout << "The last person remaining is: " << lastPerson << endl;
return 0;
}