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