inteuler(int n) { int res = n; // res代表最终结果(result) for (int i = 2; i * i <= n; ++i) // 从2开始遍历所有可能的质因数 { if (n % i == 0) // 若i是n的因数 { // 欧拉函数的计算公式:φ(n) = n * (1 - 1/p1) * (1 - 1/p2) * ... * (1 - 1/pk) // 上式通分,等价于 res = res / i * (i - 1) res = res / i * (i - 1); // 因为与指数无关,所以通过循环除掉所有的i因子 while (n % i == 0) n /= i; } } // 如果最后剩下的n大于1,说明n本身是一个质数,即上面的for循环不起作用 // 应用欧拉函数的公式处理最后一个质因数 if (n > 1) res = res / n * (n - 1); return res; }
intjosephus(int n, int m){ int res = 0; // 0是n=1的情况,因为只有一个人的时候那个人必然是幸存者 for (int i = 2; i <= n; i++) { // 约瑟夫问题的递推关系式: // J(n,m) = (J(n-1,m) + m) % n res = (res + m) % i; } return res + 1;//加一是因为从1开始记录 }
关键步骤:J(n,m) = (J(n-1,m) + m) % n 这里假设第一个人从0开始计数,那么第一轮淘汰的是m-1号,即(m-1) mod n 剩余(n-1)个人重新编号,被淘汰者的下一位(即 m % n 号)成为新编号 0,被淘汰者的下一位(即 m % n 号)成为新编号 0。 在 n-1 个人的新问题中,幸存者的编号是J(n-1,m). 那我们将它映射回n个人的原始编号: 新编号 0 对应原始编号 m % n。 新编号 1 对应原始编号 (m+1) % n。 … 新编号 k 对应原始编号 (m + k) % n。 因此,幸存者在 n 个人时的原始编号为:J(n,m) = (J(n-1,m) + m) % n (这里的J(n-1,m)即上一行中的k)。 在程序中就表示为res = (res + m) % i;
模拟法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include<vector> usingnamespace std;
intjosephus(int n, int k){ vector<int> people(n); for (int i = 0; i < n; i++) people[i] = i; // 编号 0 到 n-1
vector<int> q1(n), q2(n); for (int i = 0; i < n; i++) cin >> q1[i]; for (int i = 0; i < n; i++) cin >> q2[i]; vector<int> merged; int i = 0, j = 0; while (i < n && j < n) { if (q1[i] < q2[j]) { merged.push_back(q1[i++]); } else { merged.push_back(q2[j++]); } } // 处理剩余元素 while (i < n) merged.push_back(q1[i++]); while (j < n) merged.push_back(q2[j++]);