Josephus problem(约瑟夫问题)

Josephus problem(约瑟夫问题)

问题描述

链接:http://bailian.openjudge.cn/practice/2746/

约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。

来源:

  • 约瑟夫是犹太军队的一个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余人,他们都是宁死不屈的人,所以不愿投降做叛徒。一群人表决说要死,所以用一种策略来先后杀死所有人。
  • 于是约瑟夫建议:每次由其他两人一起杀死一个人,而被杀的人的先后顺序是由抽签决定的,约瑟夫有预谋地抽到了最后一签,在杀了除了他和剩余那个人之外的最后一人,他劝服了另外一个没死的人投降了罗马。

那么如何选择一个位置以使得你变成那剩余的最后一人?

这个问题可以转换成递推问题:$J(n,k)=(J(n-1,k)+k)%n$

  • $J(n,k)$表示人数为n,报数节点为k,最后剩下的人的编号。
  • 对于$J(n,k)$,可以转换成 $J(n-1,k)$
  • 当人数为$n$时,进行第一次报数,第$k$个人退出,此时剩余$n-1$个人。
  • 若重新排号,第$k+1$个人为1号,那么此时问题为 $J(n-1,k)$,而此时求出的结果的编号是人数为$n-1$并且重新排号的情况,$J(n,k)$与$J(n-1,k)$编号的关系为 $J(n,k)=(J(n-1,k)+k)%n$ (自行体会)。

注意事项:重新编号应该从0开始,最后要和题目的编号方式比较再输出。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int n, m;

    scanf("%d %d", &n, &m);
    
    while(n!=0&&m!=0)
    {
        int temp = 0;
 
        for(int i=1; i < n; i++)//从1推到n
        {
            temp = (temp + m) % (i + 1);
        }
  
        printf("%d\n", temp+1);
 
        scanf("%d %d", &n, &m);
    }
        return 0;
}


约瑟夫变体:求最后x个的编号

链接:https://vjudge.net/problem/UVA-1452

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int n, m;
    int T;
    scanf("%d", &T);
    for (int i = 0; i < T;i++)
    {
        scanf("%d %d", &n, &m);
        int temp ;
        temp =((m % 3)+2)%3;//剩3个时的编号
        
        for (int i = 3; i < n ; i++) //从3推到n
        {
            temp = (temp + m) % (i + 1);
        }
        printf("%d ", temp + 1);

        temp = ((m % 2)+1)%2;
        
        for (int i = 2; i < n; i++) //从2推到n
        {
            temp = (temp + m) % (i + 1);
        }
        printf("%d ", temp + 1);
        temp = 0;
        for (int i = 1; i < n; i++) //从1推到n
        {
            temp = (temp + m) % (i + 1);
        }
        printf("%d\n", temp + 1);
    }  
        return 0;
}

posted @ 2020-06-26 04:48  ArtistArthur  阅读(108)  评论(0)    收藏  举报