1. 程式人生 > >2016BIT小學期——博弈DP(汙神Alice和Bob)

2016BIT小學期——博弈DP(汙神Alice和Bob)

接著上面的來講

上面我們說了貪心的答案是7,我們貪心的取法是這樣的

Alice->10,10

Bob->7,7

Alice->5,5

Bob->2,2

Alice->1,1

但是題意不是這樣子的,題意的是我們每次只關心當前的決策

所以說,正確的理解思路下的選取方案應該是

Alice->10,10為了讓下一次分差最大(10-7>7-5>=5-2)

Bob->7,7,5,5(計分只記5)為了讓下一次分差最大(5-2>7-5)

Alice->2,2

Bob->1,1

正確的答案是sum=10-5+2-1=6

所以說,貪心的思路實在錯誤的理解了題目那句核心的話之下做出的錯誤的答案

很是揪心,我WA在這上面這麼多次卻渾然不知

正解思路:動態規劃

孟義超大神的一句話點醒了我,本題的DP的遞迴性質

因為本題中我們每次所做的決策都無後效性,和最優子結構很相像,我在孟義超大神的指引下走上了DP這條正確的求解答道路上

首先,在這裡,要想真的清楚的理解DP的性質,我們必須盡力拋開所謂的Alice和Bob兩個只知道嘿嘿嘿的汙神

我們這裡只認一個叫做先手的人

還是拿剛才我們的例子來講解

1 1 2 2 5 5 7 7 10 10

定義狀態:

dp:這個叫做先手的人在取該位置上的元素可以造成的最大的分差(最終的結果)

狀態轉移方程:

dp[0]=0 根據定義顯而易見

dp[i]=dp[i-1]>data[i]-dp[i-1]?dp[i-1]:data[i]-dp[i-1]

我知道大家肯定一眼都看不懂,我來解釋一下

先明確,這裡沒有Alice也沒有Bob,只有先手

當先手取完之後,肯定會造成分差,如果是Bob先取的話,那麼Bob造成的分差肯定是正的,對Alice來說肯定是負的,同理,如果是Alice先取的話,那麼Alice造成的分差肯定是正的,對Bob來說,這個分差肯定是負的

這麼來說,比如現在先手取位置i,那麼對於位置i-1來說,該先手肯定是後手,那麼dp[i-1]對於這個當前取位置i的先手來說就是負的,我們的dp[i]的值很明顯就是data[i]-dp[i-1](含義就是,我當前要把我之前的負逆差通過data[i]彌補回來)。

但是這樣子就簡簡單單的求出來dp[i-1]了嗎,並不是

假如說當前我計算出來的data[i]-dp[i-1]並沒有dp[i-1]大,俺麼這代表什麼麼意思呢

這樣子代表的就是我們先手只取位置i的元素並不是最優的,我們可以通過先手取位置i和位置i-1(只以位置i-1的元素計分)元素獲得更大的分差

實際上,思維敏感的同學很快就會發現,實際上,這意思就是,我們當前取位置i的先手轉換到取位置i-1的先手上了

總之,對於這兩種情況,我們取最大的一個

直到最後,我們求解出dp[n]時代表的含義就是先手取第n位置之後我們可以得到的最大的分差(就是最大的結果)

那麼現在我們再來考慮Alice和Bob這兩個汙神,題中已經說了,Alice實現首,那麼這個取位置n的先手就是Alice,我們直接輸出dp[n]就好

最後,為了防止有的同學還不理解中間的步驟,我把上面的例子推導一遍

1 1 2 2 5 5 7 7 10 10

  1. dp[0]=0;
  2. dp[1]=max(data[1]-dp[0],dp[0])=max(1,0)=1;
  3. dp[2]=max(data[2]-dp[1],dp[1])=max(0,1)=1;
  4. dp[3]=max(data[3]-dp[2],dp[2])=max(1,1)=1;
  5. dp[4]=max(data[4]-dp[3],dp[3])=max(1,1)=1;
  6. dp[5]=max(data[5]-dp[4],dp[4])=max(4,1)=4;
  7. dp[6]=max(data[6]-dp[5],dp[5])=max(1,4)=4;這裡的含義就是取6位置不如取5位置
  8. dp[7]=max(data[7]-dp[6],dp[6])=max(3,4)=4;這裡的含義就是取7位置不如取5位置
  9. dp[8]=max(data[8]-dp[7],dp[7])=max(1,4)=4;這裡的含義就是取8位置不如取7位置
  10. dp[9]=max(data[9]-dp[8],dp[8])=max(6,4)=6;
  11. dp[10]=max(data[10]-dp[9],dp[9])=max(4,6)=6;這裡的含義就是取10位置不如取9位置

最後的dp陣列是 0 1 1 1 1 4 4 4 4 6 6最大的取6,符合正確答案

3.AC程式碼:

只付上核心程式碼段:

for(int i=1;i<=n;i++)  
{  
      dp[i]=dp[i-1]>data[i]-dp[i-1]?dp[i-1]:data[i]-dp[i-1];  
}