51nod 1073约瑟夫环 递归公式法

约瑟夫环问题的原来描述为,设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。  稍微简化一下。

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

利用数学推导,如果能得出一个通式,就可以利用递归、循环等手段解决。下面给出推导的过程:

(1)第一个被删除的数为 (m - 1) % n。

(2)假设第二轮的开始数字为k,那么这n - 1个数构成的约瑟夫环为k, k + 1, k + 2, k +3, .....,k - 3, k - 2。做一个简单的映射。

k         ----->  0 
             k+1    ------> 1 
             k+2    ------> 2 
               ... 
               ...

k-2    ------>  n-2

这是一个n -1个人的问题,如果能从n - 1个人问题的解推出 n 个人问题的解,从而得到一个递推公式,那么问题就解决了。假如我们已经知道了n -1个人时,最后胜利者的编号为x,利用映射关系逆推,就可以得出n个人时,胜利者的编号为 (x + k) % n。其中k等于m % n。代入(x + k) % n  <=>  (x + (m % n))%n <=> (x%n + (m%n)%n)%n <=> (x%n+m%n)%n <=> (x+m)%n

(3)第二个被删除的数为(m - 1) % (n - 1)。

(4)假设第三轮的开始数字为o,那么这n - 2个数构成的约瑟夫环为o, o + 1, o + 2,......o - 3, o - 2.。继续做映射。

o         ----->  0 
             o+1    ------> 1 
             o+2    ------> 2 
               ... 
               ...

o-2     ------>  n-3

这是一个n - 2个人的问题。假设最后的胜利者为y,那么n -1个人时,胜利者为 (y + o) % (n -1 ),其中o等于m % (n -1 )。代入可得 (y+m) % (n-1)

要得到n - 1个人问题的解,只需得到n - 2个人问题的解,倒推下去。只有一个人时,胜利者就是编号0。下面给出递推式:

f [1] = 0; 
          f [ i ] = ( f [i -1] + m) % i; (i>1)

有了递推公式,实现就非常简单了,给出循环的两种实现方式。再次表明用标准库的便捷性。

对于上面第一个映射表,由映射关系可得,如果0~n-1中某人报了m-1,设这个人为x,那么原位置一定是(x+k)%n

相信大家都能看出规律,为什么要%n,因为后面的序号不会一直无限制增大,会变小,比如4,5,6,7,1,2,那么1和2就是(4+4)%7,(4+5)%7

附上代码,当然这题也可以递归推一推,但没有必要

#include <stdio.h>

using namespace std;
const int maxn=1e6+7;
int f[maxn];
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    f[1]=0;
    for(int i=2;i<=n;++i){
        f[i]=(f[i-1]+k)%i;
    }
    printf("%d",f[n]+1);
    return 0;
}
时间: 2024-07-30 12:39:55

51nod 1073约瑟夫环 递归公式法的相关文章

51Nod 1073约瑟夫环

1073 约瑟夫环 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数.问最后剩下的人的编号. 例如:N = 3,K = 2.2号先出列,然后是1号,最后剩下的是3号. Input 2个数N和K,表示N个人,数到K出列.(2 <= N, K <= 10^6) Output 最后剩下的人的编号 Input示例 3 2 Output示例 3 #include <bi

UVALive 3882--And Then There Was One+约瑟夫环问题变形

题目链接:点击进入 题目意思大概和约瑟夫环问题差不多,唯一的不同点在于起点改成了m:刚开始的时候我想直接链表模拟算了,但是后面一看,数据太大,就改用公式做了.约瑟夫环的公式是:f(n)=(f(n-1)+k)%n ,对于这个题起点为m,所以答案就会变成ans=(f(n)+m-k+1)%n; ans有可能小于0,此时我们要给他加上一个n,ans+=n. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #

约瑟夫环-公式递推法

约瑟夫问题 约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报.如此反复,最后剩下一个,求最后的胜利者. 例如只有三个人,把他们叫做A.B.C,他们围成一圈,从A开始报数,假设报2的人被杀掉. 首先A开始报数,他报1.侥幸逃过一劫. 然后轮到B报数,他报2.非常惨,他被杀了 C接着从1开始报数 接着轮到A报数,他报2.也被杀死了. 最终胜利者是C 解决方案 普通解法 刚学数据结构的时候,我们可能用链表的方法去模拟这个过程,N个人看作是N个链表节

LA3882 约瑟夫数学递归法

首先,约瑟夫环的数学优化方法为: 为了讨论方便,先把问题稍微改变一下,并不影响原意:问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 我们知道第一个人(编号一定是(m-1)%n) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):    k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2 并且从k开始报0.现在我们把他们的编号做一下转换: k --> 0 k+1 --

bnuoj Musical Chairs 约瑟夫环非递归

/*问题描述:n个人(编号0~(n1-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n1-1个人组成了一个新的约瑟夫环(以编号为k=m%n1的人开始): k k+1 k+2 ... n1-2, n1-1, 0, 1, 2, ... k-2 并且从k开始报0. 现在我们把他们的编号做一下转换: n1=n+1; k --> 0-->0 k+1 --> 1-->1 k+2 --> 2

【约瑟夫环变形】UVa 1394 - And Then There Was One

首先看到这题脑子里立刻跳出链表..后来继续看如家的分析说,链表法时间复杂度为O(n*k),肯定会TLE,自己才意识到果然自个儿又头脑简单了 T^T. 看如家的分析没怎么看懂,后来发现这篇自己理解起来更容易(...)共享一下~http://blog.csdn.net/chenguolinblog/article/details/8873444 问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 编号0~(n-1)是有意义的,因为要

约瑟夫环问题小结

一 问题描述 约瑟夫环问题的基本描述如下:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为1的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,要求找到最后一个出列的人或者模拟这个过程. 二 问题解法 在解决这个问题之前,首先我们对人物进行虚拟编号,即相当于从0开始把人物重新进行编号,即用0,1,2,3,...n-1来表示人物的编号,最后返回的编号结果加上1,就是原问题的解(为什么这么做呢,下文有解释).而关于该问题的解

小朋友学数据结构(1):约瑟夫环的链表解法、数组解法和数学公式解法

约瑟夫环的链表解法.数组解法和数学公式解法 约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的,他参加并记录了公元66-70年犹太人反抗罗马的起义.约瑟夫作为一个将军,设法守住了裘达伯特城达47天之久,在城市沦陷之后,他和40名死硬的将士在附近的一个洞穴中避难.在那里,这些叛乱者表决说"要投降毋宁死".于是,约瑟夫建议每个人轮流杀死他旁边的人,而这个顺序是由抽签决定的.约瑟夫有预谋地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了他原先的牺牲品

经典例题|约瑟夫环多方法解决

本文章将用循环链表.数组.递归以及循环方法对约瑟夫环问题进行讲解.其中链表法和数组法会对过程进行模拟,递归和循环将对约瑟夫环问题进行数学剖析. 问题描述 n个人围成圈,依次编号为1.2.3.....n,从1号开始依次报数,当报到m时,报m的人退出,下一个人重新从1报起,当报到m时,报m的人退出,如此循环下去,问最后剩下的那个人的编号是多少? 链表法 建立一个循环链表,节点的数值部分存储整数1至n,将尾部节点链接到第一个节点,每次遍历m-2步,把第m-1个节的指针域指向的节点数据打印出来,然后将m