实验8-1-8 报数 (20 分) 【约瑟夫环】

报数游戏是这样的:有n个人围成一圈,按顺序从1到n编好号。从第一个人开始报数,报到m(<n)的人退出圈子;下一个人从1开始报数,报到m的人退出圈子。如此下去,直到留下最后一个人。

本题要求编写函数,给出每个人的退出顺序编号。

函数接口定义:

void CountOff( int n, int m, int out[] );

其中n是初始人数;m是游戏规定的退出位次(保证为小于n的正整数)。函数CountOff将每个人的退出顺序编号存在数组out[]中。因为C语言数组下标是从0开始的,所以第i个位置上的人是第out[i-1]个退出的。

裁判测试程序样例:

#include <stdio.h>
#define MAXN 20

void CountOff( int n, int m, int out[] );

int main()
{
    int out[MAXN], n, m;
    int i;

    scanf("%d %d", &n, &m);
    CountOff( n, m, out );   
    for ( i = 0; i < n; i++ )
        printf("%d ", out[i]);
    printf("\n");

    return 0;
}

/* 你的代码将被嵌在这里 */

输入样例:

11 3

结尾无空行

输出样例:

4 10 1 7 5 2 11 9 3 6 8 

结尾无空行

答案如下:

/*思路分析:
    要给出的是“每个人的退出编号”, 而不是“退出的编号依次是”
    可以把退出的人的值置为一个特殊的数, 如-1, 遇到的时候便跳过
    若每退出一个人便把原先数组重新排序会变得很麻烦, 所以需要设置一个变量, 每当这个变量满“m”(游戏规定的退出位置), 便结束循环
    
    写题思路: 逐步拆解看需要那些变量, 先从内层循环写起, 完成了再往外延伸
    */
void CountOff( int n, int m, int out[] )
{
    int numeration[MAXN]; //创建一个数组, 存放这些人的编号“1~n”
    int i;
    for(i=0; i<n; i++) { //遍历读入元素
        numeration[i] = i + 1; //编号不包括0, 从1~n
    }
    int index = 0; //创建一个下标变量, 相当于“击鼓传花” 的 “花”, 代表着当前报数的人的下标
    int count = 0; //创建一个计数的变量, 每当该变量满足“读进了m位次的人”, 便结束循环
    int rank = 1; //设立一个变量表示“第几个踢出的”, 用在最后为输出的数组out[]写入编号时
    
    //明确, 总共有n个人, 所以要进行n次大的外层循环(即要报数n次, 才能把每个人的退出位次都决定
    int circulation = 0; //circulation是循环的意思, 把它作为控制外层循环的变量
    //m %= n; 
    while(circulation++ < n) { //对circulation进行自增操作, 总共循环了n遍
        //每次报数的过程如下
        for(count = 0; count < m; ) { //保证循环一定会移除出一个人
            if(numeration[index] != -1) { //把-1定为退出的人的编号
                count++; //如果不等于-1, 计数加一
            }
            index++; //下一个人报数 
            if(count == m) index--; //满足输出条件的话, 对下标进行-1来平衡28行
            //处理index 大于等于 n 了以后, 回头从下标0开始的问题
            if(index == n) index = 0; // 下标n已经属于越界, 不在数组中, 若index刚好为n, 那么置它为0
        	//上面那一行代码也可以写作 index %= n; 
		}
        // 最后把index下标的人踢出(numeration的值(编号)置为-1), 并在输出的out数组中index的对应位置写入踢出的排名
        //index --;
		numeration[index] = -1;
        out[index] = rank++; //读入踢出的位次后, 让位次自增
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
约瑟夫环(Josephus problem)是一种经典的数学问题,描述如下:有n个人围成一圈,从第一个人开始报数报到m的人出圈,剩下的人继续从1开始报数,直到所有人都出圈。问最后剩下的人是谁。 这个问题可以使用递归或循环来解决。下面是一个使用循环的解法。 首先,我们可以用一个数组表示所有人,每个元素都代表一个人,初始时所有人都在圈里。我们定义一个指针p,初始值为0,表示当前报数的人。我们不断循环报数,每报一次数,p就向右移动一位。当p移动到数组的末尾时,我们将其重置为0,表示回到圈的开头。当报数为m时,我们将当前p所指的人从数组中移除,并将数组长度减一。移除后,我们将p再次重置为当前位置,从下一个人开始报数。当数组中只剩下一个人时,该人即为最后剩下的人。 下面是一个使用Python实现约瑟夫环程序: ``` def josephus(n, m): people = list(range(1, n+1)) p = 0 while len(people) > 1: p = (p + m - 1) % len(people) people.pop(p) return people[0] print(josephus(7, 3)) # 输出4 ``` 在上面的程序中,我们定义了一个josephus函数,接受n和m作为参数。函数首先创建一个包含n个人的数组,然后在循环中不断报数,直到只剩下一个人为止。最后返回剩下的那个人。 我们可以通过调用josephus函数来模拟不同的实验。例如,我们可以让7个人围成一圈,每次报数3个人,看看最后剩下的是谁。运行上面的程序,输出4,表示最后剩下的是第4个人。 在实验中,我们可以尝试不同的n和m的值,观察最后剩下的人是否符合预期。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值