題目描述
法一:鏈表模擬(超時)
直接模擬刪除的過程,比如開始的時候是從0
位置開始遍歷,每隔m
刪除一個數,當我們在依次遍歷m-1
位置的同時,將它們依次移動到鏈表的末尾。當遍歷到m位置的時候就不添加到鏈表末尾而是直接刪除,重複此過程直到剩下最後一個數爲止。
int lastRemaining(int n, int m) {
list<int> li;
for(int i = 0; i < n; i++)
li.push_back(i);
while(li.size() != 1)
{
int cnt = m;
while(cnt--)
{
if(cnt != 0)
li.push_back(li.front());
li.pop_front();
}
}
return li.front();
}
法二:動態規劃(AC)
既然是動態歸化那我們肯定需要定義狀態、初始狀態和狀態轉移
狀態: 在這裏我們設狀態dp[i]
表示一共有i個數時最後剩下數的位置
初始狀態: 當狀態爲dp[0]
顯然不管m爲多少最後一個數的位置就是0
狀態轉移: 狀態轉移就是當狀態爲dp[i]
時怎麼過渡到dp[i+1]
,這裏根據狀態的含義我們知道dp[i]
表示的是一共有i個數時最後剩下數的位置,dp[i+1]
表示的是一共有i+1個數時最後剩下數的位置,其實很容易就能看出這兩個位置不就是剛好相差m
嗎,因此狀態轉移方程爲 dp[i] = dp[i - 1] + m;
然後我們就可以愉快的寫出下面的代碼
int lastRemaining(int n, int m) {
int lastPos = 0; // 記錄上一次最後被刪除數的位置
for(int i = 2; i <= n; i++)
{
lastPos = (lastPos + m) % i; // 由於lastPos可能會大於i所以這裏需要取模
}
return lastPos;
}