1. 程式人生 > >dp基礎之博弈型取石子

dp基礎之博弈型取石子

對於博弈型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,即必勝