约瑟夫环问题
做约瑟夫的时候被难住了一次,第一次写博客就拿约瑟夫开刀吧(坏笑),并且在网上找到的约瑟夫题解有些迷茫(我是算法小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; }
本文来自博客园,作者:江上舟摇,转载请注明原文链接:https://www.cnblogs.com/LQS-blog/p/15857642.html