算法科普:什么是约瑟夫环
发布日期:2021-05-19 23:03:46 浏览次数:31 分类:精选文章

本文共 3098 字,大约阅读时间需要 10 分钟。

约瑟夫环问题解析

问题描述

约瑟夫环(Josephus problem)是一个经典的数学问题。设想n个人围坐在圆桌旁,编号为1到n。从编号为k的位置开始报数,数到第m个人的时候,该人离开圈子。接下来的报数从下一个未离开的人继续,从1开始计数,依此类推,直到只剩下最后一个胜利者。

以10个人为例,k=3,m=3的情况下,游戏过程如下:

  • 3号被选出,离开圈子。
  • 从4号开始重新计数,6号离开。
  • 从7号开始重新计数,9号离开。
  • 从10号开始重新计数,2号离开。
  • 从4号开始重新计数,7号离开。
  • 从8号开始重新计数,1号离开。
  • 从4号开始重新计数,8号离开。
  • 从10号开始重新计数,5号离开。
  • 从10号开始重新计数,10号离开。最终剩下的胜利者是4号。

  • 数组求解方法

    解题思想

    数组求解的核心思想是用一个一维数组来记录每个人的状态。初始时,所有人都处于圈内(标记为1)。当某人被选出时,标记为0,表示已出圈。同时,报数器清零,下一个未离开的人继续从1开始报数。

    具体步骤:

  • 初始化数组flag,所有位置标记为1。
  • 维护两个变量count和num,分别记录已出圈人数和当前报数器的值。
  • 在每次循环中,遍历所有未离开的人,找到当前报数器所指的人。
  • 如果找到第m个人的位置,将该位置标记为0,count加1。
  • 当count等于n-1时,结束循环,剩下的位置即为胜利者。
  • 代码实现

    #include 
    #define N 1000000int flag[N] = {0};int main() { int n, m; scanf("%d%d", &n, &m); int i = 0, count = 0, num = 0; for (i = 1; i <= n; i++) { flag[i] = 1; } while (count < n - 1) { for (i = 1; i <= n; i++) { if (flag[i]) { num++; if (num == m) { printf("%d\n", i); flag[i] = 0; count++; num = 0; if (count == n - 1) break; } } } } for (i = 1; i <= n; i++) { if (flag[i]) { printf("The last one is: %d\n", i); } } return 0;}

    循环链表求解方法

    解题思想

    约瑟夫环问题也可以通过循环链表来模拟解决。每个节点表示一个人,next指针表示下一个在圈中的节点。当某人被选出时,删除该节点,调整next指针,形成新的环。

    代码实现

    #include 
    #include
    typedef struct node { int number; struct node *next;} Node;Node* CreatNode(int x) { Node *p = (Node*)malloc(sizeof(Node)); p->number = x; p->next = NULL; return p;}Node* CreatJoseph(int n) { Node *head, *p, *q; for (p = q = 1; p <= n; p++) { p = CreatNode(p); if (p == 1) { head = p; } else { q->next = p; q = p; } } q->next = head; return head;}void RunJoseph(int n, int m) { Node *p, *q; p = CreatJoseph(n); while (p->next != p) { for (i = 1; i < m; i++) { p = p->next; } q = p->next; p->next = q->next; free(q); printf("%d--", q->number); } printf("\n最后剩下的数为:%d\n", p->number);}int main() { int n, m; scanf("%d%d", &n, &m); RunJoseph(n, m); return 0;}

    递推公式求解方法

    解题思想

    约瑟夫环问题可以通过递推公式直接求解。已知f(n, m)表示n个人进行游戏中获胜者的编号,递推公式为:

    f(n, m) = (f(n-1, m) + m) % n

    其中,f(n-1, m)表示n-1个人时的胜利者编号。

    递归代码实现

    #include 
    int Joseph(int n, int m) { if (n <= 1 || m <= 1) return -1; if (n == 2) { if (m % 2 == 0) return 1; else return 2; } return (Joseph(n-1, m) + m - 1) % n + 1;}int main() { int n, m, x; scanf("%d%d", &n, &m); x = Joseph(n, m); printf("最后一个的编号是:%d\n", x); return 0;}

    迭代代码实现

    #include 
    int Joseph(int n, int m) { int i; int y = 1; if (m % 2 == 0) y = 1; else y = 2; for (i = 3; i <= n; i++) { x = (y - 1 + m) % i + 1; y = x; } return y;}int main() { int n, m, x; scanf("%d%d", &n, &m); x = Joseph(n, m); printf("最后一个人的编号是:%d\n", x); return 0;}

    实际应用

    约瑟夫环问题在实际生活中有广泛应用。例如,在团建活动中,可以将参赛人员按照约瑟夫环规则安排座位,确保心仪的对象和你坐在同一圈中,有机会共同进决赛圈。

    上一篇:考研:从双非到清华,努力、规划、踏实、自律
    下一篇:不会爬虫?送48本Python爬虫畅销书,助你一臂之力

    发表评论

    最新留言

    很好
    [***.229.124.182]2025年04月23日 01时43分26秒