问题描述:N个人依次围成一个圈,从1~N编号,从1号开始报数,报到M的人自杀(最后一个人不用自杀),问自杀顺序以及谁是胜利者
算法:
两种方法:一种是用计算机模拟游戏的运行过程,一种是直接推演出数学规律。这里只讨论前一种
数组模拟
用一维数组来表示这N个人的集合,由于非动态数组无法模拟现实中的集合将某一个元素清理出集合,所以可以用一个额外的状态标识数组来记录某个人是否已经自杀;虽然动态数组模拟的集合可以将元素清理出去,但时间复杂度为O(N),所以不考虑这种方法。
算法描述
/*
1、初始化编号数组,状态数组
2、repeat until 还剩下不止一个人{
定位编号,修改状态;
}
*/
void Josephus( int M, int N )
{
//初始化数组
int * Jose = (int*)malloc( sizeof( int ) * N );
bool * JoseTag = (bool*)malloc( sizeof( bool ) * N );
for( int i = 0; i < N; i++ ) {
Jose[i] = i + 1;
JoseTag[i] = true;
}
//模拟游戏进行
int start = 0; //每次报数时开始的下标{start|start belong to 0:N-1}
int end; //结束报数时的下标
//int delta = 0; //报了多少个数
int count = N; //还剩多少个人
while( count != 1 ) { //还未决出胜负
end = start;
int delta = 0;
while( delta < M ) { //要经过M个活人
end = (end + 1) % N;
if( JoseTag[end] == true )
delta++;
}
JoseTag[end] = false;
start = (end + 1) % N;
while( JoseTag[start] == false ) //当前编号的人必须是活人
start = (start + 1) % N;
printf( "%d\t", Jose[end] );
count--;
}
printf( "\n%d is the victor!\n", Jose[start] );
free( Jose );
free( JoseTag );
}
单循环链表模拟
/*
话不多说:直接建立一个单循环链表,模拟删除即可
*/
void Josephus( int M, int N )
{
List Head = (List)malloc( sizeof( Node ) );
List Rear = Head;
Head->next = NULL;
for( int i = 0; i < N; i++ ) {
List S = (List)malloc( sizeof( Node ) );
S->number = i + 1;
Rear->next = S;
Rear = S;
Rear->next = NULL;
}
Rear->next = Head->next;
free( Head );
List Start = Rear;
int count = N;
while( count != 1 ) {
for( int i = 0; i < M; ++i ) {
Start = Start->next;
}
List Temp = Start->next;
Start->next = Temp->next;
printf( "%d\t", Temp->number );
free( Temp );
count--;
}
printf( "\n%d is the victor!\n", Start->number );
free( Start );
}