约瑟夫环问题

做约瑟夫的时候被难住了一次,第一次写博客就拿约瑟夫开刀吧(坏笑),并且在网上找到的约瑟夫题解有些迷茫(我是算法小zz),所以发表一下我的见解

首先认识一下什么是约瑟夫问题:

n 个人的编号是 1~n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。
  (报数是从1报起)当报到 k 的时候,这个人就退出游戏圈。下一个人重新从1开始报数。
  求最后剩下的人的编号。这就是著名的约瑟夫环问题。

  本题目就是已知 n,k 的情况下,求最后剩下的人的编号。

  题目的输入是一行,2个空格分开的整数n, k
  要求输出一个整数,表示最后剩下的人的编号。

对于这种问题,我们可以把这这个类比成一个圆桌,每次删除固定的元素然后一直到只剩下一个元素为止,在输出这个元素

我们可以采取STL函数库中的vector,用动态数组解决这个问题,代码如下

#include<bits/stdc++.h>
using namespace std;
int main()
{
  int n,m;
  cin>>n>>m;
  vector<int>a;
  int i;
  a.clear();
  for(i=0;i<n;i++)
  a.push_back(i+1);
  int pos=0;
  for(i=0;i<n;i++)
{
  pos=(pos+m-1)%a.size();
  a.erase(a.begin()+pos);
    if(a.size()==1)
{
  cout<<a[0]<<endl;
  break;
  }
    }

return 0;
}

 

 

但是很遗憾,两个平台都给的是TLE,这是为什么呢?

其实很容易相同,在一个循环中频繁的删除元素,就要把这个元素的后面所有元素向后移或者前移动,多次移动就会导致效率很低,所以不得不思考别的解法;

分析一下:

对于任意的n,k;

我们可以设置f(q)为每次踢出第q个人最后的结果

0 1 2 3 4 5 6 .... n-1;

q q+1 q+2...........q-2(我们要kick off的是q+1这位兄台);

再次重复一下,将新的被杀的后一个人作为新的0号,于是新的如下:

0 1 2 3 4 5 .......n-2;

递推一下:

然后在想,

先是n个人玩这个游戏,编号从0->n-1,数m退出,当第一个m-1号人退出后,就变成了n-1个人玩这个游戏了,将这n-1个人重新编号,如图,会发现是由(原来的编号 - m)得到这重新编的号。那么这时问题变成了n-1个人玩这个游戏了,设f(n)表示n个人数m退出时问题的解,那么f(n-1)是n-1个人数m退出时问题的解,假设f(n-1)已求出,那么:
 f( n ) = f(n - 1) + m;
 并且是循环删除,所以
  f( n ) = (f(n - 1) + m)%n;
  那么,当得知f(n-2)的解时,反推至f(n-1)时,需要编号不超过n-1,因此等式为:
  f(n-1)=(f(n-2) + m) % n-1;
  那么推广到一般:f( n ) = (f (in- 1) + m )% n;
到最后剩下一个人的时候直接输出下标0就可以啦;

附上代码

#include<bits/stdc++.h>
using namespace std;
int yuesefu(int n,int m)
{
    if(n==1)
    return 0;
    else
    return ((yuesefu(n-1,m)+m)%n);
}
int main()
{
    int n,k;
    cin>>n>>k;
    cout<<yuesefu(n,k)+1<<endl;
    return 0;
}

 

posted @ 2022-01-30 21:34  江上舟摇  阅读(227)  评论(0编辑  收藏  举报