LeetCode 1690. Stone Game VII 石子游戲 VII
阿新 • • 發佈:2020-12-14
LeetCode
1690. Stone Game V11
此題來源於2020.12.13 周賽第三題
題目
題意
有從左到右排列整齊的n個石頭,其重量分別為a[0....n] , Bob和Alice分別按以下規則依次去將石頭取出
- 只能從剩餘石頭序列的最左或者最右選取石頭取出
- 每名選手在選取後獲得序列中剩餘石頭的重量和的分數
- Alice先手
很明顯該遊戲先手必勝,遊戲過程中Bob會採取最佳策略去最小化與Alice分數的差值,同時Alice也會採取最佳策略去最大化分數差則
求 在雙方都採用最優策略時 他們最終分數的差值
解析
通過分析題意我們可以認為這是一道動態規劃題
規劃的物件是該序列的連續子序列 到該序列
我們假設 \(dp[i][j]\) 為 面對序列\(Stones[i]\).... \(Stones[j]\) 時最終分數的差值
那麼對於Alice來說 每次選擇兩遍那個能使分數差則更大的石頭,即
\[dp[i][j]=max \begin{cases} dp[i+1][j]+Stones[i+1]+...+Stones[j] &\text 選取左邊的石頭\\ dp[i][j-1]+Stones[i]+...+Stones[j-1] &\text 選取右邊的石頭 \end{cases} \]對於Bob來說,每次選擇使分數差值最小的石頭, bob得分會使分值差值變小
\[dp[i][j]=min \begin{cases} dp[i+1][j-Stones[i+1]-...-Stones[j] &\text 選取左邊的石頭\\ dp[i][j-1]-Stones[i]-...-Stones[j-1] &\text 選取右邊的石頭 \end{cases} \]有了該狀態轉移方程可以 開始編寫程式碼
#include<vector> class Solution { public: int stoneGameVII(vector<int>& stones) { vector<vector<int> > dp(stones.size()+1,vector<int>(stones.size()+1,0)); int n=stones.size(); vector<int>sum(n+1,0); sum[0]=0; for(int i=1;i<=n;i++){ sum[i]=stones[i-1]+sum[i-1]; // 使用字首和減少運算過程a[i]到a[j]進行累加的操作 //注意!!! 此處的sum[i] 是a[0]+...+a[i-1] 並不包括 a[i], 如果包括a[i] 後續程式碼會訪問越界 } for(int j=1;j<n;j++){ //剩餘石頭個數 for(int i=0;i<n-j;i++){ //選擇起點 int k=i+j; //終點 if((n+j-1)%2==0){ //判斷輪到誰選取 dp[i][k]=max(sum[k+1]+dp[i+1][k]-sum[i+1],sum[k]+dp[i][k-1]-sum[i]); // printf("Alice 取石子\n"); }else{ dp[i][k]=min(sum[i+1]-sum[k+1]+dp[i+1][k],dp[i][k-1]+sum[i]-sum[k]); //printf("bob\n"); } } } return dp[0][n-1]; //返回最終值 } };
details
我們迭代中用的是剩餘石頭個數和起點作為迭代物件
因為我們規劃的目標為長度為原石頭陣列長度且起始點和終點都為原石頭陣列的起始和終點 的值
故此處應該從這兩個變數入手,從低到高進行迭代從而得到答案