約瑟夫問題

  聽說著名猶太曆史學家 Josephus有過如下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,因而決定了一個自殺方式,41我的排成一個圓圈,由第1我的開始報數,每報數到第3人該人就必須自殺,而後再由下一個從新報數,直到全部人都自殺身亡爲止。然而Josephus 和他的朋友並不想聽從。首先從一我的開始,越過k-2我的(由於第一我的已經被越過),並殺掉第k我的。接着,再越過k-1我的,並殺掉第k我的。這個過程沿着圓圈一直進行,直到最終只剩下一我的留下,這我的就能夠繼續活着。問題是,給定了和,一開始要站在什麼地方纔能避免被處決?Josephus要他的朋友先僞裝聽從,他將朋友與本身安排在第16個與第31個位置,因而逃過了這場死亡遊戲。node

  這個問題是計算機和數學中經典的一個計數問題,在算法的中這種相似的問題被叫作與瑟夫環。算法

  與瑟夫問題有不少的解決思路,好比最多見的使用循環鏈表來解決。數組

  

#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

 

  我最開始見到這個問題,使用遞歸來解決這個問題。ide

 

#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

 

  還可使用數組模仿循環鏈表來實現。spa

 

#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
相關文章
相關標籤/搜索