Joseph Problem(解约瑟夫问题)
? 今天在一個OJ上做了一個Joseph Problem(解約瑟夫問題)的題,題目不難,直接用循環鏈表模擬實際操作即可完成,但是用此種方法的時間太長,超時,所以我就用了一個大家對這類問題比較常用的解法——數學方法。
問題再現:
題目內容: 實作Joseph problem. 假設一開始有N個人,編號1~N, 按照順序以順時針圍成一個圓圈。 游戲開始時,編號1的人拿刀。 之后每一輪刀子會被往下傳M個人, 而當輪最后拿到刀子的人會將他的下一個人殺掉, 殺完后刀子會再傳給被殺的下一個人。 這樣一輪就算結束。 游戲會進行許多輪,直到只剩下最后一個人。范例1:N=5, M=2 第一輪:刀子傳給3號,4號被殺,刀子再傳給5號 (1 2 3 5) 第二輪:刀子傳給2號,3號被殺,刀子再傳給5號 (1 2 5) 第三輪:刀子傳給2號,5號被殺,刀子再傳給1號 (1 2) 第四輪:刀子傳給1號,2號被殺,最后1號存活。 范例2:N=4, M=3 第一輪:刀子傳給4號,1號被殺,刀子再傳給2號 (2 3 4) 第二輪:刀子傳給2號,3號被殺,刀子再傳給4號 (2 4) 第三輪:刀子傳給2號,4號被殺,最后2號存活。輸入格式: 輸入第一行為一個數字T,代表測資的筆數。 接下來會有T筆測資,每一筆測資一行, 會有兩個數字N,M,數字間以空格區隔。 數字范圍: T < 1000 0 < N <= 1000 0 < M <= 1000輸出格式: 輸出一行數字,將每筆測資最后存活下來的人的編號加總。輸入樣例: 3 5 2 4 3 8 4輸出樣例: 4 時間限制:1000ms內存限制:32000kb算法實現:
 - 循環鏈表(真實模擬)算法
 
 
 第一點想到的實現算法就是完全模擬問題中的方法進行算法設計,代碼如下:
#include <stdio.h>int arrQ[1000]; //循環隊列,此處用固定值 int Qcount, head, tail; //隊列操作 void add(int x, int size){if(Qcount == 0){arrQ[0] = x;}else{tail = (tail + 1) % size;arrQ[tail] = x;}Qcount ++; }void del(int size){head = (head + 1) % size;Qcount --; }int main(){int T, N, M, sum = 0;int i, j, k, tmp;scanf("%d", &T);for(i = 0; i < T; i ++){scanf("%d", &N);scanf("%d", &M);Qcount = head = tail = 0;for(j = 0; j < N; j ++){ //初始隊列 add(j + 1, N);}while(Qcount != 1){ //直到剩下最后一個人 for(k = 0; k <= M; k ++){ //根據題意 執行M+1次操作 tmp = arrQ[head];del(N);add(tmp, N);}del(N);}sum += arrQ[head]; }printf("%d", sum);return 0; } 此算法雖然很容易理解,但是時間復雜度是O(nm),執行大隊列時會超時。 - 數學推導進行求解
 
 
 
 
 這里先拿一個經典的例子進行說明。
問題:有n個人站成環 從1開始報數,報k的人出列,之后下一個人報1,問最后存在的是誰?
 
最后存在的是7。
這里可以用 f(n, k)來描述每一輪的操作,n是當前隊列中的人數,k是出列的人,f(n,k) = (f(n - 1,k) + k) % n,下面來實現。
最底端是 f(1,k) ?f(1,k) = 0 就是說只有一個人的時候存在者的下標是0,編號是7
向上,f(2,k) = (f(1,k) + k) % n ?= ?f(2,3)=(f(1,3) + 3) % 2 = 3 % 2 = 1,在只剩兩個人時,存在者在這一輪數組中的下標位置是1(下標位置為1 編號是7 )
 
向上,f(3,3) = (f(2,3) + 3) % 3 = 4 % 3 = 1,在只剩三個人時 在存者在這一輪數組中的下標位置是1 (下標位置為1 編號是7)
 向上,f(4,3) = (f(3,3) + 3) % 4 = 4 % 4 = 0
 ……
……
最后,f(11,3) = (f(10,3) + 3) % 11 = 6 % 11 = 6 ?(看看上面的表格第一行 下標位置為6 編號是7 )
 
 在只剩三個人時 幸存者在這一輪數組中的下標位置是0 ?(看看上面的表格 下標位置為0 編號是7 )
通過上述很容易寫出程序來:
int fn(int n,int k){int s = 0; //最后存在者的下標 for(int i=2;i<=n;i++) {s = (s + k) % i;}return s + 1; //因為是從數組下標是從0開始的,所以這里要加1 }
好了,經典問題說完后,來看一下我們這個題,把這種數學推導出來的公式移植到程序中,如下:
#include <stdio.h>int main() {int N, M, T, sum = 0;int i, s;scanf("%d", &T);while(T--) {scanf("%d %d", &N, &M);s = 0;for(i = 2; i <= N; i ++){s = (s + M + 2) % i; //因為題目中要求是從當前位置的M個位置的下一個人出列,所以這里要加2 }sum += s+1;}printf ("%d", sum);return 0; } 這里的時間復雜度是O(n),所以完勝。
 
博客名稱:王樂平博客
 博客地址:http://blog.lepingde.com
 CSDN博客地址:http://blog.csdn.net/lecepin
 
 
總結
以上是生活随笔為你收集整理的Joseph Problem(解约瑟夫问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python snap7 plc_Pyt
- 下一篇: [转]Displaying standa
