约瑟夫问题

  据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

  这个问题是计算机和数学中经典的一个计数问题,在算法的中这种类似的问题被叫做与瑟夫环。

  与瑟夫问题有很多的解决思路,比如最常见的使用循环链表来解决。

  

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
    int data;
    struct node *next;
}node;

node *create(int n)
{
    node *p = NULL, *head;
    head = (node*)malloc(sizeof (node ));
    p = head;
    node *s;
    int i = 1;

    if( 0 != n )
    {
        while( i <= n )
        {
            s = (node *)malloc(sizeof (node));
            s->data = i++;    // 为循环链表初始化,第一个结点为1,第二个结点为2。
            p->next = s;
            p = s;
        }
        s->next = head->next;
    }

    free(head);

    return s->next ;
}

int main()
{
    int n = 41;
    int m = 3;
    int i;
    node *p = create(n);
    node *temp;

    m %= n;   // m在这里是等于2

    while (p != p->next )
    {
        for (i = 1; i < m-1; i++)
        {
            p = p->next ;
        }

        printf("%d->", p->next->data );

        temp = p->next ;                //删除第m个节点
        p->next = temp->next ;
        free(temp);

        p = p->next ;
    }

    printf("%d\n", p->data );

    return 0;
}
View Code

 

  我最开始见到这个问题,使用递归来解决这个问题。

 

#include<stdio.h>

void
josephus(int *a,int n, int i)
{
    int times=1;
    while(number(a,n)>0)
    {
        if(i==n+1)
        {
            i=1;
        }
        while(a[i-1]==0)
        {
            i++;
            if(i==n+1)
            {
                i=1;
            }
        }
        if(times==3)
        {
            printf("%d->",a[i-1]);
            a[i-1]=0;
            i++;
            josephus(a,n,i);
        }
        times++;
        i++;
    }
}

int
number(int *a,int n)
{
    int i=0;
    int times=0;
    for(i=0;i<n;i++)
    {
        if(a[i]==0)
        {
            ;
        }
        else
        {
            times++;
        }
    }
    return times;
}

int main()
{
    int i;
    int n=41;
    int a[n];
    for(i=0;i<41;i++)
    {
        a[i]=i+1;
    }
    josephus(a,n,1);
    printf("\n");
}
View Code

 

  还可以使用数组模仿循环链表来实现。

 

#include<stdio.h>
#include<malloc.h>
int main()
{
    int *person,i,node,n,m;
    scanf("%d%d",&n,&m);
    person=(int*)malloc(sizeof(int)*(n+1));
    for(i=1;i<=n;i++)//初始化圈
    {
        person[i]=i+1;//i表示编号为i的人,person[i]的值表示编号为i的人的下一个人的编号
    }
    person[n]=1;//编号为n的下一个人的编号是1
    node=1;
    while(node!=person[node])//如果某个人的下一个人不是自己,意味着人数超过1人
    {
        for(i=1;i<m-1;i++)//这个循环终止于被杀的人的前一个人
        {
            node=person[node];//下一个人的编号为node,node的值来自于前一个人的person[node]
        }
        printf("%d->",person[node]);//输出被杀的人编号
        person[node]=person[person[node]];//修改被杀的人的前一个人的person[node]为被杀的人的后一个人的编号
        node=person[node];//这句话中的node是被杀的人后一个人
    }
    printf("%d",node);//输出最后幸存者的编号
    printf("\n");
    return 0;
}
View Code

 

posted @ 2017-08-20 22:23  灬F灬  阅读(273)  评论(2编辑  收藏  举报