n个人围一圈,报数,然后报到m的人退出,重新从1开始报数,以此类推,求最后留下的人的最初始的编号。
具体描述如下图所示:
算法思想:该题是一道典型的约瑟夫问题的变型,约瑟夫问题有三个解法链表模拟法,数组模拟法,公式法。详见百度百科约瑟夫。
这里所讲的是公式法。用数学方法来求解如下:
用函数表示:
F(1) = 0
F(2)=F(1)+m 表示:F(2)退出,然后F(2)的下一个为起始报数位置。
F(3) = F(2) + m 表示:第3个退出的是F(3),因为上一个报数的起始位置为F(2)的下一个。
由上可以得到递推公式:
F(i)=F(i-1)+m
因为可能会超出总人数范围,所以要求模
F(i)=(F(i-1)+m)%n
有了递推公式就可以在O(N)时间求出结果。
#include
using namespace std;
int main()
{
//m为输入的出队序号,s是当前报数数值,rem是剩余人数
int m, i, s = 0, rem;
//a[100]是标志位,如果a[i] = 1,表示编号为i的人还未退出,a[i] = 0代表编号为i的人已经退出。
int a[100];
cout << "请输入出队序号:" << endl;
cin >> m;
if (m > 100 || m <= 1)
{
cout << "ERROR!" << endl;
return 0;
}
for (int i = 0; i < 100; i++)
a[i] = 1;
s = 0;
i = 0;
rem = 100;
//该循环过程为算法核心,当剩余人数大于m,代表还有可退出的人,故循环,否则退出循环
while (rem >= m)
{
s += a[i];
if (s == m)
{
//当报数到m时,更新报数值,并将该人退出即将a[i]置为0,更新剩余人数。
s = 0;
a[i] = 0;
rem--;
}
i++;
i %= 100;
}
for (int i = 0; i < 100; i++)
if (a[i])
cout << i + 1 << ",";
cout << endl;
cout << rem << endl;
return 0;
}
该算法只需要循环遍历一次,故算法时间复杂度为O(n)。
#include
int main()
{
int n, m,i,s=0;
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++)
s=(s+m)%i;
printf("%d", s+1);
return 0;
}