1748:约瑟夫问题

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

输入
每行是用空格分开的两个整数,第一个是 n, 第二个是 m ( 0 < m,n <=300)。最后一行是:

0 0

输出
对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号
样例输入
6 2
12 4
8 3
0 0
样例输出
5
1
7

约瑟夫问题的递推公式是f[1]=0,f[i]=(f[i-1]+m)mod i。不过是一个“数据结构之指针和链表”里面的问题,所以还是先用链表和指针解决。因为要移除中间元素,所以需要一个双向链表,这里用一个数组来模拟:

1、构建结构和数组:

struct node{
    int id;
    node *pre;
    node *next;
}lst[302];

2、初始化数组元素的id:

    for(m=1;m<=302;m++){
        lst[m].id=m;
    }

3、初始化数组元素的指针:

    for(i=1;i<=n;i++){
        lst[i].next=&lst[i+1];
        lst[i].pre=&lst[i-1];
    }
    lst[n].next=&lst[1];
    lst[1].pre=&lst[n];

4、查询需要删除的元素,直到当前元素和下一个元素都指向同一个元素:

    node *cur=&lst[1];
    while(cur->next!=cur){
        for(i=2;i<=m;i++){
            cur=cur->next;
        }
        cur->pre->next=cur->next;
        cur->next->pre=cur->pre;
        cur=cur->next;
    }

此时,cur->id就是那个猴子王的初始编号。多组数据步骤2只需要进行一次,然后重复步骤3、4就可以了。但无论如何,这种方法的时间复杂度明显大于直接用数学方法进行求解,并且编码也多出很多。完整代码如下:

#include<iostream>
#include<cstring>
using namespace std;
struct node{
    int id;
    node *pre;
    node *next;
}lst[302];

int getking(int n,int m){
    int i;
    //1、设置前后指针
    for(i=1;i<=n;i++){
        lst[i].next=&lst[i+1];
        lst[i].pre=&lst[i-1];
    }
    lst[n].next=&lst[1];
    lst[1].pre=&lst[n];
    //2、查询
    node *cur=&lst[1];
    while(cur->next!=cur){
        //2.1、向后数到m
        for(i=2;i<=m;i++){
            cur=cur->next;
        }
        //2.2、更新前后元素指针和当前数1的
        cur->pre->next=cur->next;
        cur->next->pre=cur->pre;
        cur=cur->next;
    }
    return cur->id;
}
int main(){
    int m,n;
    //初始化数组里的编号
    for(m=1;m<=302;m++){
        lst[m].id=m;
    }
    //获得输入并求解
    cin>>n>>m;
    while(m!=0 && n!=0){
        cout<<getking(n,m)<<endl;
        cin>>n>>m;
    }
}

然后,我们来考虑一下不模拟报数过程来求解,当一个人出局之后,剩下n-1人继续这个游戏,直到剩余1人。递推:1人时进行这个游戏,他不会出局的,两人呢?剩下的人是下次还能报数的那个,谁下次还能报数?同样,考虑3个人的情况,可以得出递推公式。核心代码如下:

    s=0;    for(i=2;i<=n;i++){
       s=(s+m)%i;    }
    cout<<s+1;
时间: 2024-10-07 05:07:04

1748:约瑟夫问题的相关文章

一个不简洁的约瑟夫环解法

约瑟夫环类似模型:已知有n个人,每次间隔k个人剔除一个,求最后一个剩余的. 此解法为变种,k最初为k-2,之后每次都加1. 例:n=5,k=3.从1开始,第一次间隔k-2=1,将3剔除,第二次间隔k-1=2,将1剔除.依此类推,直至剩余最后一个元素. 核心思路:将原列表复制多份横向展开,每次根据间隔获取被剔除的元素,同时将此元素存入一个剔除列表中.若被剔除元素不存在于剔除列表,则将其加入,若已存在,则顺势后移至从未加入剔除列表的元素,并将其加入.如此重复n-1次.面试遇到的题,当时只写了思路,没

【c语言】数据结构(约瑟夫生者死者游戏的问题)

约瑟夫生者死者游戏:30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分:因此船长告诉大家,只有将全船一半的旅客投入海中,其余人才能幸免遇难.无奈,大家只得同意这种办法,并议定30个人围成一圈,由第一个人开始,依次报数,数到第9个人,就把他投入大海中,然后从他的下一个人开始从1数起,数到第9个人,再将她投入大海,如此循环,直到剩下15个人乘客为止.问哪些位置是将被扔到大海的位置. 解法有许多种,可以用数组,应为涉及到删除操作,数组(顺序线性表)比较麻烦,但不必要删除,只需要给跳船的人(元素

算法系列:约瑟夫斯问题

约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环. 有{\displaystyle n}个囚犯站成一个圆圈,准备处决.首先从一个人开始,越过{\displaystyle k-2}个人(因为第一个人已经被越过),并杀掉第k个人.接着,再越过{\displaystyle k-1}个人,并杀掉第k个人.这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着. 问题是,给定了{\displaystyle n}和{

ytu 1067: 顺序排号(约瑟夫环)

1067: 顺序排号Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 31  Solved: 16[Submit][Status][Web Board] Description 有n人围成一圈,顺序排号.从第1个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来的第几号的那位. Input 初始人数n Output 最后一人的初始编号 Sample Input 3 Sample Output 2 HINT Source freepro

约瑟夫环 C语言 单循环链表

/*---------约瑟夫环---------*/ /*---------问题描述---------*/ /*编号为1,2,-,n的n个人围坐一圈,每人持一个密码(正整数). 一开始任选一个正整数作为报数上限值m, 从第一个人开始自1开始顺序报数,报到m时停止. 报m的人出列,将他的密码作为新的m值,从他的下一个人开始重新从1报数, 如此下去,直至所有人全部出列为止.试设计一个程序求出列顺序.*/ /*---------问题分析---------*/ /*n个人围坐一圈,且不断有人出列,即频繁

用ArrayList(解决约瑟夫问题)

约瑟夫问题(Josephus problem)又称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,约瑟夫问题类似问题又称为约瑟夫环."丢手绢问题". 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身

约瑟夫问题的变种 LA3882

题目大意: N个数排成一圈,第一次删除m,以后每k个数删除一次,求最后一被删除的数. 如果这题用链表或者数组模拟整个过程的话,时间复杂度都将高达O(nk),而n<=10000,k<=10000 目测会直接TLE. 那么有没有其他的方法呢?答案是有的. 我们先忽略掉m, 分析一下每k个数删除一次,那就是经典的约瑟夫问题了. 那么,将每个数(1~n)按顺序编号为0~n-1 设第一个删除的数的编号为x,则x= k %n-1 (注意是编号,真正删除的数为编号+1) 那么剩下的n-1个数可以组成一个新的

And Then There Was One(约瑟夫问题变形)

题目链接:http://poj.org/problem?id=3517 And Then There Was One Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 5014   Accepted: 2685 Description Let’s play a stone removing game. Initially, n stones are arranged on a circle and numbered 1, …

循环单向链表(约瑟夫环)

#include <stdio.h> #include <stdlib.h> typedef struct List { int data; struct List *next; }List; //创建循环单向链表n为长度 List *list_create(int n) { List *head, *p; int i; head = (List *)malloc(sizeof(List)); p = head; p->data = 1; //创建第一个结点 for (i =