本文将用数组求解约瑟夫环问题。
题目描述
编号为1,2,3,……n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m的值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。试设计一个程序求出所有人出列的顺序。
输入格式
输入人数 n
输出格式
输出每个人的随机密码和初始密码,随后按顺序输出出列的号码及其密码。
输入输出样例
说明/提示
初始的10人和密码的随机大小都可更改,为了方便测试才设置初始值为10的。
解题思路:
创建一个大小为n的数组用来表示n个人,同时将此元素的第一和第二个域分别存放密码和下一个人的序号。同时使用计数器变量来计算报数的数字和判断是否出列。每出列一人将会把出列前一个人的next域改变,报数会跳过已出列的号码。
代码实现:
#include<iostream>
#include<iomanip>
#include<stdlib.h>
#include<time.h>
using namespace std;
int random();
int main()
{ /*Num数组的10个元素分别代表一个人的号码,元素的第一个域存放每个人的密码,第二个域存放下一个人的序号*/
/*Count为计数器,Alive为活着的人数*/
/*Pre是上一个报数的人的序号,Now是当前报数人的序号*/
int Num[10][2], Count = 0, Alive = 10, Pre = 0, Now, i, Password;
srand((unsigned)time(0)); /*设置随机数种子为时间*/
cout << " 请输入人数(最多10人):";
cin >> Alive;
if (Alive > 10 || Alive == 0)
{
/*判定人数是否超过上限*/
for (; Alive > 10 || Alive == 0;)
{
/*若超过上限则重新输入*/
cout << " 输入错误!";
cout << " 请重新输入人数(最多10人):";
cin >> Alive;
}
}
for (i = 0; i < Alive; i++)
Num[i][1] = (i + 1) % Alive; /*初始化next域*/
Password = random(); /*生成初始密码*/
for (i = 0; i < Alive; i++)
{
/*为每个号码随机分配密码*/
Num[i][0] = random();
if (Num[i][0] == Num[i - 1][0])
{
/*检查相邻号码的密码没有重复*/
for (; Num[i][0] == Num[i - 1][0];)
Num[i][0] = random();
}
cout << " " << i + 1 << "号的密码为『" << Num[i][0] << "』" << endl;
}
cout << "-------------推算开始-------------" << endl;
cout << " 初始密码为『" << Password << "』!" << endl;
cout << endl;
/*开始推算约瑟夫环的出列顺序*/
for (Now = 0; Alive != 0; Now = Num[Now][1])
{
Count++; /*报数加一*/
if (Count == Password)
{
Alive--; /*存活人数减一*/
Password = Num[Now][0]; /*更改报数上限*/
Num[Pre][1] = Num[Now][1]; /*前一个人的next域改变*/
cout << " " << Now + 1 << "号玩家报出了号码『" << Count << "』!" << endl;
cout << " " << Now + 1 << "号玩家已出列!" << endl;;//输出这一次出列的人的编号
if (Alive == 0)
{
cout << "-------------推算结束-------------" << endl;
}
else
{
cout << " " << "下一个密码是『" << Num[Now][0] << "』!" << endl;
}
cout << endl;
Count = 0; /*报数归零*/
}
else
{
Pre = Now; /*上一个报数的序号改变*/
}
}
}
int random()
{ //生成随机数
int a;
a = rand() % 100 + 1;
return a;
}