有一排正數,玩家A和玩家B都可以看到。
每位玩家在拿走數字的時候,都只能從最左和最右的數中選擇一個。
玩家A先拿,玩家B再拿,兩人交替拿走所有的數字,
兩人都力爭自己拿到的數的總和比對方多。請返回最後獲勝者的分數。
例如:
5,2,3,4
玩家A先拿,當前他只能拿走5或者4。
如果玩家A拿走5,那麼剩下2,3,4。輪到玩家B,此時玩家B可以選擇2或4中的一個,…
如果玩家A拿走4,那麼剩下5,2,3。輪到玩家B,此時玩家B可以選擇5或3中的一個,…
思路:
動態規劃,設定兩個狀態,
分別爲f(i,j)和 s(i,j)分別表示在 序列i..j 之間,先手能得到的最大數目以及後手計算後剩下的最大數目。
所以先手f(i,j) = max(arr[i]+s(i+1,j),arr[j]+s(i,j-1)) 表示目的爲求最大的情況
後手剩下的爲 s(i,j)= min(f(i+1,j),f(i,j-1); 而後手玩家爲了拿到更多,所以他會選擇,留給對方最少的那種情況。
通過這種相互依賴,我們可以寫出遞歸函數,並設定遞歸邊界,
在先手情況下f(i,j),如果 i==j 表示只剩下最後一個,那麼 就直接返回這個數。
在後手情況下s(i,j),如果 i==j 表示只剩下最後一個,那麼 就直接返回0。因爲後手玩家已經取完了,所以留給先手玩家的就爲0;
接着,通過畫出依賴關係,我們可以發現f和s的相互關係,並且很多情況的遞歸都是重複計算的,所以這裏就出現了無後效性的情況,即後面的計算都不會改變之前已經計算好的。就可以根據這個寫出動態規劃。
代碼:
#include<iostream>
#include<stdio.h>
using namespace std;
int arr[1000] = {0};
int N;
//設置兩個狀態圖
int dps[1000][1000]={0};
int dpf[1000][1000]={0};
int main() {
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&arr[i]);
}
for (int j=0;j<N;j++){
//先手狀態初始化
dpf[j][j] = arr[j];
for (int i = j-1;i>=0;i--){
//先手狀態和後手狀態依次計算
dpf[i][j] = max(arr[i]+ dps[i+1][j],arr[j]+dps[i][j-1]);
dps[i][j] = min(dpf[i+1][j],dpf[i][j-1]);
}
}
printf("%d\n",dpf[0][N-1]);
return 0;
}