約瑟夫環問題

江上舟搖發表於2022-01-30

做約瑟夫的時候被難住了一次,第一次寫部落格就拿約瑟夫開刀吧(壞笑),並且在網上找到的約瑟夫題解有些迷茫(我是演算法小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;
}

 

相關文章