博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
约瑟夫环问题
阅读量:4963 次
发布时间:2019-06-12

本文共 2305 字,大约阅读时间需要 7 分钟。

先看下资料,数学公式来的

资料转自:

约瑟夫环问题是一道经典的数据结构题目

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
一般我们采用一个循环队列来模拟约瑟夫环的求解过程,但是如果n比较大的时候,采用模拟的方式求解,需要大量的时间来模拟退出的过程,而且由于需要占用大量的内存空间来模拟队列中的n个人,并不是一个很好的解法。
在大部分情况下,我们仅仅需要知道最后那个人的编号,而不是要来模拟一个这样的过程,在这种情况下,可以考虑是否存在着一种数学公式能够直接求出最后那个人的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):

我们先看第一个人出列后的情况,显而易见,第一个出列的人的编号一定是m%n-1,这个人出列后,剩下的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 --- 1
k+2 --- 2
...  ....
k-2 -- n-1
可以看出,这就是原问题中把n替换成n-1的情况,假设我们已经求出来在这种情况下最后胜利的那个人的编号是x,那个倒推回去的那个人的编号就正好是我们要求的答案,显而易见,这个编号应该是(x+k)%n
那么如何知道n-1个人下面的这个x呢,yes,就是n-2个人情况下得到的x'倒推回去,那么如何知道n-2情况下的x'呢,当然是求n-3个人,这就是一个递归的过程
f(1) = 0(f(1)就是现在还剩下1个人,那么无论m为几,这个人总会出列,因此f(1)=0)
f(n) = (f(n-1)+m)%n
那么我们要求f(n),就从f(1)倒推回去即可
int f(int n, int m)
{
   int r = 0;
   for(int i = 2; i <= n; i++)
       r = (r + m) % i;
   return r + 1; //这是因为日常生活中编号总是从1开始
}

 

基本的约瑟夫环问题,可以直接用数组模拟,也可以用数学方法做,当然,时间差很大

ContractedBlock.gif
ExpandedBlockStart.gif
pku 3715
数学方法 #include
using namespace std; int main() {
int n,k,m; while(scanf("%d %d %d",&n,&k,&m),n||k||m) {
int i,d,s=0; for(i=2;i<=n;i++) s=(s+k)%i; k=k%n;if(k==0) k=n; d=(s+1)+(m-k); if(d>=1 &&d<=n) printf("%d\n",d); else if(d<1) printf("%d\n",d+n); else if(d>n) printf("%d\n",d%n); } return 0; } ------------------------------------------------------------------------------ 数组模拟 #include
using namespace std; int a[10001]; int main() {
int n,k,m; while(scanf("%d %d %d",&n,&k,&m),n||m||k) {
int i,cur; for(i=0;i

 

 

可以先打表,在求m值的时候,可以枚举每一种可能

ContractedBlock.gif
ExpandedBlockStart.gif
hdu 1443
#include
int f[15]; bool jos(int ,int ); int main() {
int i,j,k,m,n; for(i=1;i<14;i++) for(j=i;;j++) if(jos(i,j)) {
f[i]=j; break; } while(scanf("%d",&n),n) printf("%d\n",f[n]); return 0; } bool jos(int n,int m)//n代表每一边的人数,m代表所报的数 {
int start=0,end=n-1,killed; int i,j; bool flag=true; for(i=2*n;i>n;i--)//从头进行模拟 {
killed=(m-1)%i;//i代表剩余的人数,killed表示出列的相对编号 if((killed<=end)&&(killed>=start)) {
flag=false; break; } //重新修改符号条件的范围 start=((start-m)%i+i)%i;//按照那个编号变化公式重新进行赋值 end=((end-m)%i+i)%i;//加上i,再%i的目的是保证它非零 } return flag; }

转载于:https://www.cnblogs.com/nanke/archive/2011/09/06/2168796.html

你可能感兴趣的文章
IOS-UI-UILable
查看>>
bzoj1345
查看>>
对员工宽容的公司 都死掉了
查看>>
python基础五
查看>>
BZOJ 1013: [JSOI2008]球形空间产生器sphere
查看>>
DevExpress TreeList添加右键菜单问题
查看>>
AEAI Portal V3.5.2门户集成平台发版说明
查看>>
[转]我们这么努力,也不过是为了成为一个普通人。
查看>>
G面经prepare: Chucked Palindrome
查看>>
CSS3 -webkit-transform
查看>>
在Linux系统里安装Virtual Box的详细步骤
查看>>
手动卸载的vs2010
查看>>
C#_初识之HelloWorld
查看>>
QT5:先导篇 加载资源
查看>>
Linux的日常(1)--Linux系统
查看>>
[leetcode]Construct Binary Tree from Preorder and Inorder Traversal
查看>>
玩转游戏搜索
查看>>
关于HTML之拖动
查看>>
[Toolchain]arm-none-linux-gnueabin编译
查看>>
静态博客阅读次数与评论解决方案
查看>>