1. 程式人生 > >【LeetCode & 劍指offer刷題】發散思維題3:62 圓圈中最後剩下的數字(約瑟夫環問題)

【LeetCode & 劍指offer刷題】發散思維題3:62 圓圈中最後剩下的數字(約瑟夫環問題)

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...)

62 圓圈中最後剩下的數字(約瑟夫環問題)

 

題目描述

每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作為牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號為0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試著想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)   方法一:用陣列模擬過程(也可以用連結串列模擬)
/* 約瑟夫環問題 方法一:用陣列模擬過程,刪除之後剩下的元素即為最後結果(可用狀態陣列) 81ms,有點慢 O(nlogm(n)), O(n) */ class Solution { public :     int LastRemaining_Solution (
int n , int m )     {         vector < int > state ( n , 1 ); //定義一個數組儲存各元素的狀態         int count_del
= 0 ; //對刪除的數字計數         int count = 0 ; //用於數哪個數被刪除         int i = 0 ; //索引         for (; count_del < n ; i ++)         {             if ( i == n ) i = 0 ;             if ( state [ i ] == 1 ) count ++;             if ( count == m ) //第m個數時進行“刪除”             {                 count = 0 ;                 count_del ++;                 state [ i ] = 0 ; //更改狀態,0表示已經被刪除             }         } //退出迴圈時,最後一個元素狀態被置0,i++會執行一次,故應返回i-1         return i - 1 ;     } };   方法二:找遞推公式   現在先將n個人按照編號進行排序:  0 1 2 3 … n-1  那麼第一次被淘汰的人編號一定是K-1(假設K < n,若K > n則為(K-1) mod n)。將被選中的人標記為”#”:  0 1 2 3 … K-2 # K K+1 K+2 … n-1  第二輪報數時,起點為K這個候選人。並且只剩下n-1個選手。假如此時把k看作0’,k+1看作1’…  則對應有: 0 1 2 3 ... K- 2 # K K+1 K+2 ... n-1 n-K ' n-2' 0 ' 1' 2 ' ... n-K-1'

 

此時在0’,1’,…,n-2’上再進行一次K報數的選擇。假設f[n-1]的值已經求得,因此我們可以直接求得當選者的編號s’。  但是,該 編號s’是在n-1個候選人報數時的編號,並不等於n個人時的編號 ,所以我們還需要將s’轉換為對應的s。  通過觀察, s和s’編號相對偏移了K,又因為是在環中,因此得到 s = (s'+K) mod n。  f[n] = (f[n-1] + k) mod n   /* 方法二:推出遞推公式(動態規劃) i = 1, res =0 i = 2, res = (0+3)%2 = 1 i = 3, res = (1+3)%3 = 1 i = 4, res = (1+3)%4 = 0 ... O(n),O(1) */ class Solution { public :     int LastRemaining_Solution ( int n , int k )     {         if ( n < 1 || k < 1 ) return - 1 ; //返回-1表示非法輸入                 int   last = 0 ;         for ( int i = 2 ; i <= n ; i ++)             last = ( last + k ) % i ; //i個人時刪除數的索引等於i-1個人時刪除數的索引+k(再對i取餘)                 return last ;     } };