2021-09-02 约瑟夫问题,java实现(个人详解,相信一定能看懂)

本文详细介绍了约瑟夫问题,通过动态规划的方法求解最后的胜利者。分析了问题的数学模型,指出利用动态规划可以有效解决此问题。代码部分展示了如何在Java中实现该算法,同时讲解了可能的易错点,强调寻找胜利者在初始数组中的位置索引而非编号。最后,提醒读者可通过画图辅助理解问题。
摘要由CSDN通过智能技术生成

约瑟夫问题(个人详解,相信一定能看懂)

问题描述

约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。

分析过程

构造链表法时间复杂度过大,不做讨论。
数学解法:使用动态规划求解。这里令:f(n,m)表示在n个人里从1循环报数,报数到m的人离开,直到最后只剩下一个人,其返回值为最后一个人在这初始n个人里的索引(索引每离开一个人,就会变一次)。那么People[f(n,m)]就是这个胜利者。那么:考虑f(n-1,m)与f(n,m)是否有关系?如果有关系,那么就可以构造递推关系式,使用动态规划的方式去求解胜利者了。
因为每次离开一个人,报数会从这个人的下一个人重新开始,那么在下一轮的报数开始,索引0的位置处就是离开人的下一位。假设离开者在上一轮的位置索引是t,那么它的下一位在上一轮的索引是t+1。在这位离开者离开后,他的下一位在新一轮里的索引是0。也就是索引减少了t。那么这个t与m有什么关系?下面分情况讨论:如果m小于当前层的人数,那么t=m。如果m大于当前层的人数numbers,那么t=m%numbers。也就是说:去除离开者后,其它当前轮的幸存者们的索引会统一减去 m%numbers(将两种情况总结后的结论,对于离开者前面的人,索引还要加上numbers)。那么反推:已知离开后的每个人的索引,他们在上一轮(加入上一位离开者)后,在上一轮的索引位置是什么?推理可得:index_new=(index_old+m%numbers_new)%numbers_old=(index_old+m)%numbers_old。

自已画的示意图,很丑
基于这种情况,就可以反向推导出最后的索引。

代码

对于1,…,n的初始编号,报数m离开,代码如下:

public int getPerson(int n,int m){
    int index=0; //f(1,m)的索引位置,因为只剩下一个人,索引必为0
    for(int i=2;i<=n;i++){
        index=(index+m)%i;
    }
    return index+1;//数字编号与索引的关系正好是大于1关系
}

易错点讲解

本题的目标是找到胜利者在初始数组(没有人离开时)的位置索引,并最终通过这个位置索引确定谁是胜利者,而不是胜利者的编号。可以把开始没报号的人想象成一个数组,我们的目的并不是维护一个确定不变的数,而是说这个数组大小会改变,胜利者在这个数组中的位置索引也会跟着数组大小改变而改变(增减某一个人)。因此找的规律其实是胜利者位置索引的变化规律。

这里定义了:每轮第一个报“1”的人会自动变成此次报数过程中的索引0位置。整个动态规划过程是在这个基础上取求解的。而往往题设中的数字编号:1,2,3,4…n会导致迷惑,其实把他们换成a,b,c,d…也可以,只要记住这个数组的排列顺序,最后就可以使用索引取求解。用数字编号简化了这个过程。

可以通过画图的方式取帮助理解。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值