好久沒有看有關算法的問題了,今天廢了不少勁,再感嘆一句:要想學好算法就要常練習,沒什么捷徑可走。廢話不多說,如下:
問題描述:有m個人,圍成一個環,編號為 0、1、2、3、、、m-1,從第一個人開始循環報數,假設數到n的那個人出列,然后從下一個人繼續數數,數到n出列,以此循環,最后那個人為勝利者,求勝利者的編號。
分析如下:
設m為人的個數 n為要數的數 k為從第幾個人開始數
第一次的數列,記為A
0 1 2 3 4 5 6 7 8 9 、、、n%m k、、、m-2 m-1
假設第一次出列了一個人,則編號肯定為n%m-1(減1因為從零開始)。k=n%m,第一次出列后的數列為
0 1 2 3 4 5 6 7 8 9 、、、k、、、m-2 m-1
第二次從k開始數數那么可以組成新的數列,記為數列B
k->0
k+1->1
k+2->2
k+3->3
、
、
、
k-3->m-3
k-2->m-2
如果我們知道了數列B的最終勝利者是的編號是x,那么x在原來數列A中的編號是多少呢?很容易算出來:(x+k)%m,而k=n%m,替換后為(x+n%m)%m=(x+n)%m,(x+n)%m為數列A的勝利者。那么x又該如何求呢,我們可以求數列C,就這樣這么以次類推。直到只有一個人時,勝利者的編號肯定為0.
假設f(y)為勝利者:則有
f(1)=0;
f(2)=(f(1)+n)%2;
f(y)= (f(y-1)+n)%y;(公式) y為數列的人數 n為要數的數
以下為編程實現,將f(y)替換為number y替換為i
/*********************************************************************** **m總人數,則標號為0~m-1 n為要數的數 **成功返回序號1~m,失敗返回-1 ***********************************************************************/ int winner(int m, int n) { int i; int number; if (m <= 0 || n <= 0) { return -1; } number = 0; /* 當只有一個人時,編號為0的出圈 */ for (i = 2;i <= m;i++) { /* 循環m-1次將剩下一個人 */ number = (number + n % i) % i; /* 這樣寫易理解,或(number+n)%i */ } return number + 1; /* 程序從0編號,返回時應+1 */ }
