约瑟夫环(丢手绢问题)
文章目錄
- 問題描述
- 思路
- 代碼實現
問題描述
有 1~N 個數字,從 1~m 依次報數,數到 m 的數字要被刪掉,求最后剩下的數字是?
思路
| 1 | n-m+1 |
| 2 | n-m+2 |
| … | … |
| m-2 | n-2 |
| m-1 | n-1 |
| m | 被刪掉了 |
| m+1 | 1 |
| m+2 | 2 |
| … | … |
| n-1 | n-1-m |
| n | n-m |
通過上面的表格,我們可以發現這樣的規律:
將某數字第一次報數設為 first ,第二次報數設為 second 。那么存在這樣的關系:first=(second+m?1)%n+1first = (second + m - 1) \% n + 1first=(second+m?1)%n+1(公式一)
為什么不是 first=(second+m)%nfirst = (second + m) \% nfirst=(second+m)%n (公式二)呢?其實一開始我確實總結出來的是公式二,但是發現有個漏洞,數字編號是從 1 開始的,而公式二的編號是從 0 開始的,具體來說,就是當 second = n-m 時,first = 0 。可以看到并不符合實際,first=n 才對。換言之,也就是如果我們從 0 開始計數,那么公式二是可用的,如何從 0 開始計數呢? 答案就是把數字序列存到數組里嘛~
因此,將公式二可以進化為 first=(second+m)%n+1first = (second + m) \% n + 1first=(second+m)%n+1(公式三),但是簡單的為公式二的結果 +1 ,就導致在公式二中,本來只有 second = n-m 結果不符合實際,而在公式三中,變成了只有 second = n-m 的結果符合實際,原本沒問題的都變得有問題了……這是因為我們沒有做到加減均衡,只有 +1,而沒有 -1 。因此公式一應運而生~
那么我們可以得到這樣的規律:
當n>1時,f(n)=(f(n?1)+m?1)%n+1當 n>1 時,f(n) = (f(n-1) + m - 1) \% n + 1當n>1時,f(n)=(f(n?1)+m?1)%n+1
當n=1時,f(1)=1當 n=1 時,f(1) = 1當n=1時,f(1)=1
解釋一下就是,剩最后一個數的時候直接返回最后一次報數為 1 的數,反之則需要繼續刪除一個數。
而當 n-1=1 時,f(n) 也就意味著,最后一次報數為 1 的數,倒數第二次報數的時候它報的是幾。
那么對于 f(n-1) 的調用也就意味著,本次報數為 n 的數,下一次報數為幾。
說到這里已經很清楚了,顯而易見的遞歸思想。
代碼實現
int m; int yuesefu(int n){if(n == 1) return 1; // 最后一次報數為 1,開始回溯return (yuesefu(n-1) + m - 1) % n + 1; // f(n-1)==1的時候開始回溯,求最后一次報數為 1 的數,第一次報數為幾? }// 還可以再簡化 int m; int yuesefu(int n){return n == 1 ? 1 : (yuesefu(n-1) + m - 1) % n + 1; }// 如果將數字序列存入數組中,則可用公式二 int m; int yuesefu(int n){return n == 0 ? 0 : (yuesefu(n-1) + m) % n; }總結
以上是生活随笔為你收集整理的约瑟夫环(丢手绢问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 35岁和25岁哪个水多(女性30岁水多)
- 下一篇: 香农信息熵之可怜的小猪