dp基礎之博弈型取石子
阿新 • • 發佈:2018-11-27
對於博弈型dp,一般是從複雜走向簡單,故不從最後一步開始分析,反而從第一部開始分析
問題:一排N個硬幣,兩人先後從最右邊取一個或兩個硬幣,規定取走最後石子的人為勝。
問先手是否必勝(先手必勝:True;先手必敗:False)
例:
N = 5
輸出:True,先手必勝,第一個人先取兩個石子,無論後手怎麼取,最終都是先手拿到最後的石子
問題分析:
要求面對N個石子,是否先手必勝
需要知道面對N-1和N-2個石子,是否先手必勝
子問題:
設f[i]表示對面i個石子,是否先手必勝(f[i] = True/False)
f[i] = True , f[i-1] = False and f[i-2] = False ;拿一顆石子或兩顆石子都輸
True , f[i-1] = False and f[i-2] = True ;拿一顆石子輸拿兩顆石子贏
True , f[i-1] = True and f[i-2] = False ;拿一顆石子贏,拿兩顆石子輸
False , f[i-1] = True and f[i-2] = True ;拿一顆石子或兩顆石子都贏
其實就是:對於對面i顆石子的人來說,只要你拿一顆或者兩顆石子對手都贏,則你必輸。故:
f[i] = False if f[i-1] = True and f[i-2] = True else True
初始條件:
f[0] = False,面對0顆石子,先手什麼也不能做,必敗
f[1] = f[2] = True
轉移方程:
f[i] = False if f[i-1] = True and f[i-2] = True else True
計算順序:
f[0]....f[N]
時間複雜度O(N),空間複雜度O(N)
程式碼及註釋如下:
def take_coins(N): if N == 0: return False if N <=2 : return True #f[1] = f[2] = True f = [True for i in range(N+1)] for i in range(3,N+1): f[i] = False if f[i-1] == True and f[i-2] == True else True return f[N] N = 5 print(take_coins(N)) #結果:True,即必勝