约瑟夫环问题(O(n)解法)

约瑟夫环问题(O(n)解法)


假设n个人,报数为m的被淘汰,求最后剩下的人。

暴力解法是O(m*n)的。


转自:http://blog.csdn.net/lishuzhai/article/details/51125072


题目:

n个人围成一个圈,每个人分别标注为1、2、...、n,要求从1号从1开始报数,报到k的人出圈,接着下一个人又从1开始报数,如此循环,直到只剩最后一个人时,该人即为胜利者。例如当n=10,k=4时,依次出列的人分别为4、8、2、7、3、10,9、1、6、5,则5号位置的人为胜利者。给定n个人,请你编程计算出最后胜利者标号数。(要求用单循环链表完成。)

第一行为人数n; 
第二行为报数k

10
4


公式法(即递推):

递推过程:


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


(2)设第二次的开始数字为k,

  做下映射:(即将数字的排列计算还是从0开始)

  k--->0

  k+1--->1 

 k+2--->2

  ---  ---

  k-2--->n-2 


此时剩下n-1个人 ,假如我们已经知道了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) 


这个公式的思想:


现在假设n=10

0 1 2 3  4 5 6 7 8 9 

k=3 

第一个人出列后的序列为:

 0 1 3 4 5 6 7 8 9 

即:  3 4 5 6 7 8 9 0 1(1式) 


我们把该式转化为: 0 1 2 3 4 5 6 7 8 (2式) 


则你会发现: ((2式)+3)%10则转化为(1式)了 


 也就是说,我们求出9个人中第9次出环的编号,最后进行上面的转换就能得到10个人第10次出环的编号了

 设f(n,k,i)为n个人的环,报数为k,第i个人出环的编号,则f(10,3,10)是我们要的结果


 当i=1时,  f(n,k,i) = (n+k-1)%n

 当i!=1时,  f(n,k,i)= ( f(n-1,k,i-1)+k )%n

 copy
  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.     int n, m,i,s=0;  
  5.     scanf("%d%d",&n,&m);  
  6.     for(i=2;i<=n;i++)  
  7.         s=(s+m)%i;  
  8.     printf("%d", s+1);  
  9.     return 0;  
  10. }  

说一下:

 for(i=2;i<=n;i++)
        s=(s+m)%i;

这个式子:

首先从2开始,因为1个人的时候报的数字的人为0号,结果已经确定了。不需要从i=0开始,要注意的是序列从0开始编号的,所以最后的输出结果也要加1.

s表示的是上一轮的结果,m代表是每多少个人出列一次,i代表当前已经出列了多少个人。

整个式子就是根据上一个的出列数和已经出列的人数来算的。

如果还不懂就仔细琢磨哦。






  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值