首页 > 试题广场 >

环形链表的约瑟夫问题(进阶)

[编程题]环形链表的约瑟夫问题(进阶)
  • 热度指数:1915 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号

输入描述:
一行两个整数 n,m,n 表示链表的长度,m 表示每报数到 m 就自杀。


输出描述:
输出最后存活的人的编号(编号从 1 开始到 n)。
示例1

输入

5 2

输出

3

备注:
java解决
##类似新旧数组变化求余
import java.util.Scanner;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int s = 0;
        for(int i = 2;i <= n;i++) {
            s=(s+m)%i;
        }
        System.out.println(s+1);     
    }  
}
发表于 2019-10-16 21:00:42 回复(1)
刚学的单向环形链表解决约瑟夫问题,结果是符合,就是超时了
但是在本地跑n=500万,秒出的
您的代码已保存
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为28.57%

import java.util.Scanner;
// 一行两个整数 n,m,n 表示链表的长度,m 表示每报数到 m 就***。
public class Main {
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        
       
        Person killed = null;    // ***的人
        Person aheadKilled = null;    // ***之前的人,因为是单项环形链,要想形成链就得知道最后一个人,也就是***的人之前的那个人
        Person newPerson; // 临时变量,新Person,放在循环之外
        // 先组成一个链表
        // 创建N个结点,形成单项环形链
        for (int i = 1; i <= n; i ++) {
            newPerson = new Person(i);
            // 如果是第一个人,那么自己和自己形成环形链
            if (killed == null) {
                killed = newPerson;    // 默认***的就是第一个人
                aheadKilled = newPerson;    // ***之前的那一个人也是自己,因为就剩一个人了
                killed.next = newPerson;    // 自己的下一个人指向自己
            } else {    // 如果不是第一个人,则在末尾处添加新的人
                aheadKilled.next = newPerson;    // 原***之前的那一个人的后一个人指向新Person
                aheadKilled = newPerson;    // 新的***之前的那一个人指向新的Person
                newPerson.next = killed;    // 新Person的后一个人指向***的人
            }
        }
        
        // 如果环内所剩之人一直大于1人,那么一直查数
        // killed与aheadKilled指针相同时,证明环内只剩一人
        while (killed != aheadKilled) {
            // 每报数到m就***,从1开始数,killed实际上移动了m-1次,例如每报数3就***,那么killed指针移动了3-1次,即1->2,2->3共2次
            // 相对应的,***之前的一个人也移动m-1次
            for (int i = 0; i < m - 1; i ++) {
                killed = killed.next;
                aheadKilled = aheadKilled.next;
            }
            // 移动完毕之后,此时killed指针指向的Person即为***之人,将此人从环中除去,即killed后移一位
            killed = killed.next;
            // 重新形成环
            aheadKilled.next = killed;
        }
        
        // 此时最后剩下的killed或aheadKilled指向的Person即为最后剩余之人
        System.out.print(killed.number);
    }
}

class Person {
    
    public Person(int number) {
        this.number = number;
    }
    
    int number;   // 编号 
    Person next;    // 下一个人
}
编辑于 2020-02-01 17:39:58 回复(1)
#include <stdio.h>

int get_num(int n, int m) {
    if (n == 1) {
        return 1;
    }
    return (get_num(n - 1, m) + m - 1) % n + 1;
}

int main(void) {
    int n, m;
    scanf("%d%d", &n, &m);
    printf("%d\n", get_num(n, m));
    return 0;
}

发表于 2022-05-08 20:48:06 回复(0)
今天递归爆栈了,不用递归就ok
let [n,m]=readline().split(' ').map(item=>parseInt(item))
const listNode=function(val){
    this.val=val
    this.next=null
}

let head=new listNode(-1)
let p=head
for(let i=0;i<n;i++){
    const node=new listNode(i+1)
    p.next=node
    p=node
}
p.next=head.next
head.next=null
let q=p
p=q.next

// 计算每轮生存节点
// const getLive=function(n,m){
//     if(n==1) return 1
//     else  return (getLive(n-1,m)+m-1)%n+1
// }

// const compute=function(head,m){
//     if(head==null||head.next==head||m<1) return head
//     let count=getLive(n,m)
//     while(count>1){
//         head=head.next
//         count--
//     }
//    console.log(head.val) 
// }
const compute=function(){
    if(head==null||head.next==head||m<1) return head
      let count=1
    for(let i=2;i<=n;i++){
        count=(count+m-1)%i+1
    }
  
//     while(count>1){
//         head=head.next
//         count--
//     }
   console.log(count) 
}
compute()


编辑于 2021-07-02 14:07:06 回复(0)
发表于 2020-07-22 18:01:14 回复(2)

利用递推解决约瑟夫环问题

n, m = map(int, input().split())
live = 0
for i in range(2, n+1):
    live = (live+m)% i    # 活下来的犹太人(同一个人a)倒数第i轮中所处的位置与倒数第i-1轮中所处的位置之间的关系
print(live+1)
发表于 2020-04-05 18:11:29 回复(0)
n,m=map(int,input().split(" "))
r=0
for i in range(2,n+1):
        r=(r+m)%i
print(r+1)
约瑟夫问题有数学公式可以带入计算,正常循环的方式反而会导致超时
发表于 2020-03-23 10:50:37 回复(0)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    
    public static int josephusKillFast(int n, int m) {
        if (n == 1) {
            return 1;
        }
        int result = (josephusKillFast(n - 1, m) + m) % n;
        return result == 0 ? n : result;
    }
    
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String[] parameters = bufferedReader.readLine().split(" ");
        int n = Integer.parseInt(parameters[0]);
        int m = Integer.parseInt(parameters[1]);
        System.out.println( josephusKillFast(n, m));
    }
}

不通过
您的代码已保存
请检查是否存在数组越界等非法访问情况
case通过率为28.57%
发表于 2019-12-26 21:08:04 回复(1)

问题信息

上传者:小小
难度:
8条回答 4567浏览

热门推荐

通过挑战的用户

查看代码