【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’,…,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
;
}
};