題目來源:力扣
這是我做的第一道動態規劃題目,今天看了一上午算法導論動態規劃那一章,下午準備檢驗一下自己的理解.雖然這道題難度爲簡單級別,但在自己獨立思考下做出來了,感覺美滋滋!
題目描述:
愛麗絲和鮑勃一起玩遊戲,他們輪流行動。愛麗絲先手開局。
最初,黑板上有一個數字 N 。在每個玩家的回合,玩家需要執行以下操作:
選出任一 x,滿足 0 < x < N 且 N % x == 0 。
用 N - x 替換黑板上的數字 N 。
如果玩家無法執行這些操作,就會輸掉遊戲。
只有在愛麗絲在遊戲中取得勝利時才返回 True,否則返回 false。假設兩個玩家都以最佳狀態參與遊戲。
==========================================================
示例 1:
輸入:2
輸出:true
解釋:愛麗絲選擇 1,鮑勃無法進行操作。
示例 2:
輸入:3
輸出:false
解釋:愛麗絲選擇 1,鮑勃也選擇 1,然後愛麗絲無法進行操作。
==========================================================
審題:
由於題目的主要標籤是動態規劃,因此我直接嘗試從動態規劃的角度去思考該問題.
題目中的關鍵信息是最後一句:兩個玩家都以最佳狀態參與遊戲.我們知道,動態規劃算法通常與最優解問題相關.當然要使用動態規劃算法來解決問題,還需要檢查其他必要條件是否滿足,後面一一進行分析.
該問題中,玩家以最佳狀態參與遊戲意味着玩家會在每一步選擇最好的結果,以保證其取得最終勝利.我們使用布爾數組P[]
表示存儲玩家在最優狀態下參與遊戲的最優結果.P[i] = true
表示若當前黑板上數字爲i,則在兩個玩家都以最佳狀態參與遊戲的情況下,當前玩家會取得勝利.反之,則表示當前玩家最終會失敗.
我們首先分析該問題是否具有最優子結構形式.由於兩個玩家都以最佳狀態參與遊戲,因此該問題天然地即是最優子結構形式.如果當前黑板上數字爲N,假設當前用戶的最優選擇爲x,則如果後續玩家都已最佳狀態參與遊戲,則有:,表示如果對手能夠贏得最終勝利,則當前用戶最終將失敗,如果對手最終失敗,則當前用戶將贏得最終勝利.如果當前用戶無法執行有效選擇,則其最終將失敗.
接下來分析該問題的子問題是否具有重疊,假設當前黑板上數字爲N, 則爲了確定最優的選擇,我們需要判斷每一可能的x(其中N%x==0),計算P[N-x].因此,在計算P[N-x1]時,我們需要計算P[K], K<N-x1, 而當計算P[N-x2]時,我們也要計算P[K], K<N-x2.因此子問題存在重疊結構.
在以上分析中,我們如果黑板上有數爲N,則有N個子問題,P[1], P[2], …P[N], 我們可以使用自底向上的動態規劃算法解決該問題.
java算法:
class Solution {
//假設布爾數組P存儲用戶最優選擇下最終勝利結果.
//p[i] = true表示如果當前用戶的選擇範圍爲0~i,則在自己與對手均以最佳狀態參與時,她將勝利
//p[i] = false表示如果當前用戶的選擇範圍爲0~i,則在自己與對手均以最佳狀態參與時,她將失敗
//當N=1時,用戶無法選擇有效值,因此p[1] = false
//當N=2時,該用戶當前步選擇1,對手最優結果爲p[1] = false,因此該用戶勝利,因此p[2] = true
//當N=3時,該用戶當前步只能選擇2,對手最優結果爲p[1] = true,因此該用戶失敗,因此p[3] = false
//當N=4時,該用戶當前步可以選擇1,2,如果選擇1,對手最優結果爲p[3] = false, 如果選擇2,對手最優結果爲p[2] = true,
//因此他當前步最優選擇爲1,p[4] = true
public boolean divisorGame(int N) {
boolean[] play = new boolean[N+1];
play[1] = false;
for(int i = 2; i <= N; i++){
for(int j = 1; j < i; j++){
if(i % j == 0){ //如果可以選擇j
if(play[i-j] == false) //如果下一步對手的最優執行結果爲false,則當前步執行結果爲true
play[i] = true;
}
}
}
return play[N];
}
}