华为笔试编程2

题目描述:

n个人围一圈,报数,然后报到m的人退出,重新从1开始报数,以此类推,求最后留下的人的最初始的编号。
具体描述如下图所示:
华为笔试编程2_第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;  
}  

你可能感兴趣的:(C++,算法)