约瑟夫问题

5 篇文章 0 订阅
4 篇文章 0 订阅

约瑟夫问题是经典的循环链表。
1)内容: 约瑟夫(Joseph)问题的一种描述是:编号为1,2,…, n 的n 个人按顺 时针方向围坐一圈, 每人持有一个密码(正整数)。一开始选任一个正整数作为报数上限值m, 从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将它的密码作为新的m值,再从下个人开始新一轮报数,如此反复,直到剩下最后一人则为获胜者。试设计一个程序求出出列顺序。
2)要求: 利用单向循环链表存储结构模拟此过程, 按照出列的顺序印出各人的编号。
3) 测试数据: n=7,7 个人的密码依次为:3,1,7,2,4,8,4 。m的初值为20,则正确的出列顺序应为6,1,4,7,2,3,5

#include <stdio.h>
#include <stdlib.h>
// 结构体的定义
typedef struct Person {
    int order;
    int password;
    struct Person * next;
} Person;

Person * pHead = NULL;  // 链表的第一个节点 
Person * pTail = NULL;  // 链表的尾部    
Person * per = NULL;    // 用于遍历 

int n, m;

// 下面是函数的声明
void Create();  // 创建一个链表
void Joseph(int m); // 约瑟夫问题的执行函数
int main(void)
{
    // n为总人数, m为初始密码
    scanf("%d %d", &n, &m);
    Create(); // 创建链表
    per = pHead;  // 将用于遍历的节点指向头节点,因为下面的这个函数使用递归写的
    Joseph(n);
    return 0;
} 

void Create() {
    // 定义一个为节点
    Person * tail;
    for (int i = 0; i < n; i++) {
        Person * person;
        person = (Person *) malloc(sizeof(Person));
        person->order = i + 1;
        scanf("%d", &person->password);
        if (pHead == NULL) {
            pHead = person;
        } else {
            tail->next = person;
        }
        tail = person;
    }
    // 因为是循环链表,所以必须将尾与头连接起来
    tail->next = pHead;
    // 将函数中的尾节点赋给全局变量中的尾节点
    // 这个在后面有用
    pTail = tail;
}
/*
* 因为最后最后每一个人都会报数(最后一个报数的为获胜者)
* 一次递归出列一个人,所以要需要递归n次
*/
void Joseph(int number) {
    // 当number为0时结束递归
    if (number == 0) {
        return;
    }
    // 用于报数,第一个人报1所以初始值为1
    int count = 1;
    // p用于记录删除的节点
    Person * p = NULL;
    // 前面记录的为指针的用处就在这里
    // 当m=1时,第一个人出列,所以就不用进行下面的循环
    // 但是当m不等于1时,找到那个要出列的节点
    while (count != m) {
        per = per->next;
        pTail = pTail->next;
        count++;
    }
    // p为要删除的节点
    p = per;
    // 重置m
    m = p->password;
    // 从链表中删除p
    pTail->next = per->next;
    // 下面的是输出出列人的编号
    // 因为有些ACM要求,用空格间隔,所以最后一个不能有空格
    if(number == 1)
        printf("%d", p->order);
    else
        printf("%d ", p->order);
    per = per->next;
    // 释放p
    free(p);
    // 进行下一个递归
    Joseph(number - 1); 
}

约瑟夫排列问题

对于给定的1,2,3,…,n中的k个数,Josephus想知道是否存在一个正整数m(m≤n),使得Josephus(n,m)排列的最后k个数恰好为事先指定的k个数。例如当n为7,k为4,指定排列的最后k个数为7,5,1,4时;由于(7,3)Josephus排列为3,6,2,7,5,1,4;所以求得m值为3。

这个约瑟夫排列问题,每个人都没有密码,只有一个初始值,当报到这个人时出列。要求找一个满足题目要求的初始值m。

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

typedef struct Person {
    int order;
    struct Person * next;
} Person;

Person * pHead = NULL;  // 链表的第一个节点 
Person * pFront = NULL;
Person * per = NULL;
int n, m;
int b[100], k;
// 函数
void Create(); 
void Joseph(int number, int m);
int main(void)
{
    scanf("%d %d", &n, &m);
    int i;
    int a[n];
    for (i = n - m ; i < n; i++) {
        scanf("%d", &a[i]);
    }
    for (i = 1; i <= n; i++) {
        pHead = NULL;
        pFront = NULL; 
        k = 0;
        int flag = 1;
        Create();
        per = pHead;
        Joseph(n, i);
        for (int j = n - m; j < n; j++) {
            if (a[j] != b[j]) {
                flag = 0;
            }
        }
        if (flag == 1) {
            printf("%d", i);
            return 0;
        }
//      printf("\n");
    }
    printf("0");
    return 0;
} 

void Create() {
    Person * tail;
    for (int i = 0; i < n; i++) {
        Person * person;
        person = (Person *) malloc(sizeof(Person));
        person->order = i + 1;
        if (pHead == NULL) {
            pHead = person;
        } else {
            tail->next = person;
        }
        tail = person;
    }
    tail->next = pHead;
    pFront = tail;
}

void Joseph(int number, int m) {
    if (number == 0) {
        return;
    }
    int count = 1;
    Person * p = NULL;
    while (count != m) {
        per = per->next;
        pFront = pFront->next;
        count++;
    }
    p = per;
    pFront->next = per->next;
    if(number == 1) {
        b[k++] = p->order;
//      printf("%d", p->order);
    }
    else {
//      printf("%d ", p->order);
        b[k++] = p->order;
    }
    per = per->next;
    free(p);
    Joseph(number - 1, m); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值