【约瑟夫环】
问题描述
约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。
通过链表解决(待更新)
通过数组模拟解决(待更新)
通过数学推导公式解决
思路
首先,我先剧透一下推导公式:
- f(N,M)f(N,M)表示,N个人报数,每报到M时杀掉那个人,最终胜利者的编号
- f(N−1,M)f(N−1,M)表示,N-1个人报数,每报到M时杀掉那个人,最终胜利者的编号
下面我们来模拟一下n=7,m=3时的场景:
表示7个人,他们先排成一排,假设每报到3的人被杀掉。
刚开始时,头一个人编号是1,从他开始报数,第一轮被杀掉的是编号3的人。
编号4的人从1开始重新报数,这时候我们可以认为编号4这个人是队伍的头。第二轮被杀掉的是编号6的人。
……
第六轮时,编号1的人开始重新报数,这时候我们可以认为编号1这个人是队伍的头。这轮被杀掉的是编号5的人。
下一个人还是编号为1的人,他从1开始报数,不幸的是他在这轮被杀掉了。
最后的胜利者是编号为4的人。
将表格当成数组,并翻译后:
- f(1,3)f(1,3) :只有1个人了,那个人就是获胜者,他的下标位置是0
- f(2,3)=(f(1,3)+3)%2=3%2=1f(2,3)=(f(1,3)+3)%2=3%2=1:在有2个人的时候,胜利者的下标位置为1
- f(3,3)=(f(2,3)+3)%3=4%3=1f(3,3)=(f(2,3)+3)%3=4%3=1:在有3个人的时候,胜利者的下标位置为1
- f(4,3)=(f(3,3)+3)%4=4%4=0f(4,3)=(f(3,3)+3)%4=4%4=0:在有4个人的时候,胜利者的下标位置为0
。。。 - f(7,3)=4;
通过上面的推导我们发现了很多共同点,总结起来就是f(N,M)=(f(N−1,M)+M)%N(N个人报数,每报到M时杀掉那个人,最终胜利者的编号)。
好了,公式也推的差不多了。没时间多说了,上代码:
- 代码演示
代码1:
#include <iostream>
using namespace std;
int main()
{
int n, k, x = 0;
cout << "请输入总共人数:";
cin >> n;
cout << "请输入k,确定第几个人:";
cin >> k;
for (int i = 2; i <= n; i++)
{
x = (x + k) % i;
}
cout << "最后的幸存者是:" << x + 1 << endl;
return 0;
}
运行结果如下:
代码2
#include<iostream>
#include<stdio.h>
using namespace std;
int yuesefu(int n, int m)
{
if(n == 1)
{ //这里返回下标,从0开始,只有一个元素就是剩余的元素0
return 0;
}
else
{
return (yuesefu(n - 1, m) + m) % n;
}
}
int main()
{
int n, m;
cout << "请输入总共人数:";
cin >> n;
cout << "请输入k,确定第几个人:";
cin >> m;
cout << "最后的幸存者是:" << yuesefu(n, m) + 1 << endl;
return 0;
}
运行结果如下: