約瑟夫環O N 和O M N 演算法詳解

2021-07-06 02:14:22 字數 2645 閱讀 4536

問題描述:

已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列;他的下乙個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列,求最後乙個出列人的編號。

該問題可以用陣列或者迴圈鍊錶模擬,因為都是直接模擬,需要迴圈n次,每次數m次,所以時間複雜度都是o(n*m)。雖然複雜度比o(n)大,但是可以求出出圈的編號順序。也正是因為該方法將每次出圈的人都找了出來,所以做了很多無用操作,複雜度才會高達o(n*m)。

下面是陣列和鍊錶方法的**:

題意:n個人,從第k個人開始數1,數到m的人剔除,輸出剔除的順序。

#include 

#include

#include

#include

#include

using

namespace

std;

#define maxn 10000

int a[maxn];

int n,k,m;

bool incircle[maxn];

typedef

struct _nodenode;

node* makedll(int n)

return head;

}void josephdll(node* head, int k, int m)

/*delete operation,begin*/

q = nodetodelete->next;

q->prev = nodetodelete->prev;

nodetodelete->prev->next = q;

printf("%d ",nodetodelete->number);

if(nodetodelete == head)

free(nodetodelete);

/*delete operation,end*/

nodetodelete = q;/*count from the next node*/

}printf("%d\n",head->number);

}void josepharr()

i--;

cout

<1)<

incircle[i%(n+1)]=0;/*delete the member i%(n+1) from the circle*/

}}int main()

cout

<

josepharr();/*base on the array*/

for(int i=1;i<=n;i++)

node* head;

head = makedll(n);

cout

josephdll(head,k,m);/*base on the doubly linked lists*/

return

0;}

o(n)方法運用動態規劃,或者說遞推,或者說找規律,都行吧。雖然複雜度低,但是不能求出出圈的順序。如果需要求出出圈的順序,複雜度依然是o(n*m)。

該方法遞推公式網上很多都能找到,但是卻極少人給出遞推過程。

我們發現,當從圈出來乙個人之後,後乙個人需要從1開始重新計數直到計到m。例如有n個人(1,2,3,… ,n-2,n-1),第m個人出來之後,需要從第m+1開計數。如果從m+1個人重新編號(即m+1個人編號為0,m+1為2,… …),那麼對剩下的n-1個人的操作跟開始有n個人的時候操作就是一樣的。

這就是可以運用動態規劃的原因:n的情況跟n-1的情況存在著某種關係。而我們只需用動態方程將這種關係表達出來,問題就可以解決。

下面顯示了如何進行重新編號:

設n個人圍成乙個圈的時候出列是第x』號,n-1個人圍成乙個圈的時候出列的是x號,並假設x已經求出來,那麼根據上面的遞推公式就可以反推求x』: 因為x=(x』*n+n-m)%n=x』*n%n+n%n-m%n=x』-m%n

所以x』=x+m%n=(x+m)%n

如果定義:dp[n]表示圈裡有n個人的時候最後剩下那個人的編號,答案就是dp[n]。

那麼就有dp[n]=(dp[n-1]+m)%n

但是dp[n-1]並不知道,所以dp[n]也沒法求出。當然dp[n-1]也需要通過動態方程求出:

dp[n-1]=(dp[n-2]+m)%n。

同樣dp[n-2],dp[n-3]….分別需要用dp[n-3],dp[n-4]求出。

最後dp[2]需要dp[1]求出,然而dp[1]=0是已知的。

所以從dp[2]開始依次可以求出dp[2],dp[3],dp[4]…dp[n]。

那麼問題就解決了。

#include 

#include

#include

#include

#include

using

namespace

std;

#define maxn 10000

int dp[maxn];

int joseph(int m, int n)

return dp[n];

}int main()

約瑟夫環演算法

約瑟夫環是乙個數學的應用問題 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,直到圓桌周圍的人全部出列。2b求解 private void myjosf int teamle...

演算法 約瑟夫環

已知n個人 以編號1,2,3 n分別表示 圍坐在一張圓桌周圍 使用 list 來模擬環結構,不被淘汰則將其加入到 list 尾部,淘汰的直接移除。需注意,以編號 k 為第乙個報數的人,需要調整其在報數正式開啟前到 list 的頭部。param n 人的總數 param k 開始報數的序號,1 k n...

約瑟夫環問題詳解

講乙個比較有意思的故事 約瑟夫是猶太軍隊的乙個將軍,在反抗羅馬的起義中,他所率領的軍隊被擊潰,只剩下殘餘的部隊40餘人,他們都是寧死不屈的人,所以不願投降做叛徒。一群人表決說要死,所以用一種策略來先後殺死所有人。於是約瑟夫建議 每次由其他兩人一起殺死乙個人,而被殺的人的先後順序是由抽籤決定的,約瑟夫...