問題分析:
這是一道博弈型dp,其實質上是區間型dp,目標是讓自己拿到的數字和不必對手小
設己方數字和是a,對方是b,目標是a>=b,等價於a-b>=0
也就是說,如果兩人都存著自己與對手的數字和之差,sa = a-b,sb = b-a,先手目標是讓sa最大化,後手目標讓sb最大化
當一方x面對剩下的數字時,他的目標就是最大化sx = x-y,當x取走乙個數字m後,對手y此時也變成先手,sy = y-x
對於x來說
sx = -sy+m = m-sy
m是當前要取的數字,sy是對手的數字和
現在x有兩種選擇,取序列a的頭或者尾,因為是最優策略,x選取的m = max、
case1:如果x取的是a[0],則y面對的是a[1],...,a[n-1],則y的目標是最大化sy,則x想要最大化sx = -sy+a[0] = a[0]-sy
case2:如果x取走的是a[n-1],則y面對的是a[0],...,a[n-2],則y的目標是最大化sy',則x想要最大化sx' = -sy'+a[n-1] = a[n-1]-sy'
當y面對x取走乙個數字後,此時y是先手,他的目標同樣是最大化sy,但此時y面對的序列是a[1],...,a[n-1]或者是
a[0],...,a[n-2]
子問題:設f[i][j]表示先手面對序列a[i][j]時能得到的與對手的數字差。
f[i][j] = max
max只有乙個數字a[i]時,己方得到數字a[i],對方沒有數字了,得0,數字差為a[i]
f[i][i] = a[i] (i = 0,...,n-1)
計算順序:
序列長度1:f[0][0],f[1][1],......,f[n-1][n-1]
序列長度2:f[0][1],f[1][2],...,f[n-2][n-1]..
.序列長度n-1:f[0][n-2],f[1][n-1]
序列長度n:f[0][n-1]
答案:f[0][n-1]>=0,則返回true否則返回false
時間和空間複雜度都是o(n^2),空間複雜度可以優化到o(n)
**及注釋如下:
def take_coins_inturn(a):
n = len(a)
if n == 0:
return true
f = [[0 for i in range(n)] for j in range(n)]
#初始化f[i][i] = a[i]
#長度1
for i in range(n):
f[i][i] = a[i]
#長度2,..n
for l in range(2,n+1):
for i in range(0,n-l+1):
j = i+l-1
#f[i][j] = max
f[i][j]= max(a[i]-f[i+1][j] , a[j]-f[i][j-1])
#只要f[0][n-1]大於等於0,說明先手必勝,否則先手必輸
return true if f[0][n-1] >= 0 else false
a = [1,5,233,7]
print(take_coins_inturn(a))
#結果:true
優化空間解釋:
本來要計算矩陣f是n*n的,計算順序是從f[i][i]組成的對角線開始,向右上計算過去,和dp基礎之最長回文子串裡的優化空間解釋圖一樣
每次計算f[i][j]時,和本行的f[i][j-1]、下一行的f[i+1][j]有關(只和f[i][j]左邊乙個數和下邊乙個數有關),且算完f[i][j]時,
f[i][j-1](f[i][j]左邊的那個數)不會再用到,因此,f[i][j]直接覆蓋f[i][j-1](左邊那個數)。壓縮到一行就是新計算出來的f[i]覆蓋到原來的f[i]
**及注釋如下:
def take_coins_inturn(a):
n = len(a)
if n == 0:
return true
f = [0 for i in range(n)]
#初始化f[i][i] = a[i]
#長度1
for i in range(n):
f[i] = a[i]
#長度2,..n
for l in range(2,n+1):
for i in range(0,n-l+1):
j = i+l-1
#f[i][j] = max
f[i]= max(a[i]-f[i+1] , a[j]-f[i])
#只要f[0][n-1]大於等於0,說明先手必勝,否則先手必輸
return true if f[0] >= 0 else false
a = [1,5,233,7]
print(take_coins_inturn(a))
#結果:true
dp基礎之博弈型取石子
問題分析 要求面對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 fals...
dp基礎之劃分型劃分最小劃分次數
確定狀態 最優策略中最後一段回文串是s j.n 1 需要知道s前j個字元 0.j 1 最少可以劃分成幾個回文串 子問題 設 s前i個字元 0.i 1 最少可以劃分成f i 個回文串 f i min 初始條件 f 0 0,前0個即空串是0 計算順序 f 0 f n 判斷回文串 分為奇數長度回文串和偶數...
dp基礎之雙序列型子串出現次數
問題分析 可以用類似於最長公共子串的思路。問b在a中出現的次數,考慮最後乙個字元b n 1 和a m 1 case1 如果b n 1 a m 1 則考慮b 0,n 2 在a 0,m 2 出現多少次 case2 如果b n 1 a m 1 則考慮b 0,n 1 在a 0,m 2 出現多少次 子問題 原...