约瑟夫环

萝らか妹 提交于 2019-11-27 20:44:53

参考博客:

1、约瑟夫环问题详解

2、约瑟夫环


 

 

约瑟夫环问题

什么是约瑟夫环问题

  约瑟夫是犹太军队的一个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余人,他们都是宁死不屈的人,所以不愿投降做叛徒。一群人表决说要死,所以用一种策略来先后杀死所有人。于是约瑟夫建议:每次由其他两人一起杀死一个人,而被杀的人的先后顺序是由抽签决定的,约瑟夫有预谋地抽到了最后一签,在杀了除了他和剩余那个人之外的最后一人,他劝服了另外一个没死的人投降了罗马
  我们这个规则是这么定的:
  在一间房间总共有n个人(下标0~n-1),只能有最后一个人活命
按照如下规则去杀人:

  1. 所有人围成一圈
  2. 顺时针报数,每次报到q的人将被杀掉
  3. 被杀掉的人将从房间内被移走
  4. 然后从被杀掉的下一个人重新报数,继续报q,再清除,直到剩余一人

 



推导过程:

特例1:q=2,n=2^k

#n = 20   1   ==>   0#n = 40   1   2   4   ==> 0   2  ==>   0#n = 80   1   2   3   4   5   6   7   ==> 0   2   4   6   ==> 0   4   ==> 0

定义 Jq=(n=) 为n个人,每次杀死第q个人构成的约瑟夫环最后结果,则有jq=2(n=2^k) = 0

 

 


 

 

q=2,n=任意数

 

  当n可以为任意数字的时候,就不会有上面这么简单的站位了,你的走位需要飘逸一点才能活到最后
  举个栗子:n=9
  注:示例途中的下表从1开始,我们也可以看成是从0开始,就不去改图了


  能看出来,我们干掉途中的第一个人也就是2,之后就只剩下8个人了,这时候n=8=2^3,这样一来又回到Jq=2(2^k)上了,

这时候我们需要的是找到当前的1号元素


  这时候,我们从3号开始,就成了另外一个规模小1的约瑟夫问题(恰好为2^k的特例)。
  此时,我们可以把3号看成新的约瑟夫问题中的1号位置:
  Jq=2(n=8) = Jq=2(n=2^3) = 1,也就是说这里的1代表的就是上一个问题中的3号

 

  So:Jq=2(n=9) = 3
  答案为3号
  总结下规律:
    

  在q=2的前提下,给出n,我们首先找出,离n最近的一个2^k数,如n=9,那么这个2的幂次方数就是8,同理可得。找到之后,我们需要转换成对应的这个2^k数的约瑟夫环问题,因为其第一个元素即是我们想要的答案

   Jq=2(n) = Jq=2(2^k + t) = 2t+1

 

 


 

q=任意数,n=任意数

 

  说完了特例,应该对约瑟夫环的问题了解了,现在说说q≠2的情况下,应该是什么样的规律
  我们假定:

  - n — n人构成的约瑟夫环 - q — 每次移除第q个人  
- Jq(n)表示n人构成的约瑟夫环,每次移除第q个人的解 - n个人的编号从0开始至n-1
0  1  2  ................................   n-1       总共n人|   |  |                                       |q q+1 q+2 ...... n-2  n-1   0  1  2   ......  q-2     (这里是除去q-1这位兄台的剩余n-1人)设第q个人也就是下标为q-1的那位,杀死,剩下n-1个人,如上这时,又来重复我们的老套路:将新的被杀的后一个人作为新的0号,于是新的如下:0  1  2   ......     ..........     ........  n-2

 

 

 

 

  现在大概知道我们的新的约瑟夫环的下标都是这样来的:在旧的下标基础上,减去一个q,再用计算出的结果对长度取余
  new = (old-q) % n

 

  反推一下:
  old = (new+q) % n

 


 

 1 #include<iostream>   2 #include<stdio.h>   3 using namespace std;   4    5 int yuesefu(int n,int m){   6         if(n == 1){   7                 return 0; //这里返回下标,从0开始,只有一个元素就是剩余的元素0   8         }   9         else{  10                 return (yuesefu(n-1,m) + m) % n; //我们传入的n是总共多少个数  11         }  12 }  13 int main(void){  14         int a,b;  15         cin>>a>>b;  16         cout<<yuesefu(a,b)<<endl;  17   18         //或者,直接循环迭代,求出来的result如上  19         int result = 0;  20         for(int i = 2;i <= a;i++){  21                 result = (result+b) %i;  22         }  23         cout<<"result = "<<result<<endl;  24         return 0;  25 }
View Code

 

 

 

 


 

【题目链接】https://vjudge.net/problem/51Nod-1073

首先,将n个人编号为: 0,1,2,3........k-1,k,k+1........n-1,

第一轮结束之后第k个人,也就是k-1将出列,剩下的人: 0,1,2,3.........k-2, k, k+1.......n-1

下一轮从k开始我们进行重新编号,将上面的编号为:   n-k,n-k+1,n-k+2,n-k+3.............., 0,.1,.........n-k-1,

我们如果知道了这一轮存活下来的人对应的编号是y,那么他本来的编号就是 x=(y+k)%n

由此引出递推式 f(n)=(f(n-1)+k)%n | f(1)=0

 

 

【题目链接】https://vjudge.net/problem/UVA-1394

给出n,k,m表示,第一次先出队第m个人,然后从后面的人开始数到k的出列接着从1开始数,

和原来的有了一点变化就是不是从第一个人开始数了,而是先出队第m然后从m+1开始,不过只有第一轮的和后面的公式不一样我们只要算出f(n-1)再推f(n)就好了。

我们将第m人出列后从新对余下的(n-1)人编号,f(n-1)=(f(n-2)+k)%(n-1);  设x=f(n-1),则最后的答案  ans=(x+m)%n+1;

 1 #include<bits/stdc++.h>   2 using namespace std;   3 int main()   4 {   5     int n,m,k;   6     while(~scanf("%d%d%d",&n,&k,&m),(n||m||k)){   7         int f = 0 ;   8         for(int i=2;i<n ;i++)   9             f = ( f+k ) % i ;  10         f = (f+m+n) % n + 1 ;  11         printf("%d\n",f);  12     }  13     return 0;  14 }

 

 

【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=2211

【参考博客】https://blog.csdn.net/u012717411/article/details/43925433

这个就是每次出队是k倍数的人,找到队尾之后将再次从队首开始数1,与约瑟夫有些不同,但不变的是公式推倒的过程。

想要知道f(n),我们不妨先假设得到了x=f(n-1),这里的n表示是第几轮从头开始,我们如果将这个x转为原本的编号呢,如果我们知道他前面死了几个人的话,

直接让x加上这个人数就好了,因为数到k就死一个,也就是说x前面有几个(k-1)就表示上一轮x前面淘汰的人,加上就好,最后边界f(k,k)=k

 1 #include<bits/stdc++.h>   2 using namespace std;   3 int dfs(int n,int k){   4     if( n == k ){   5         return k ;   6     }   7     int x = dfs(n-n/k,k);   8     return x + (x-1)/(k-1);   9 }  10 int main(){  11     int T,n,k;  12     scanf("%d",&T);  13     while(T--){  14         scanf("%d%d",&n,&k);  15         int ans = dfs(n,k) ;  16         printf("%d\n",ans);  17     }  18     return 0;  19 }

 

 

 

 

 

 

 

 

 

 

 

  

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!