1. 程式人生 > >演算法概論week10 | leetcode 877. Stone Game

演算法概論week10 | leetcode 877. Stone Game

題目描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

分析

這道題是比較有技巧性的。由於題目規定石子堆數為偶數,那麼可以把石子堆分成兩種堆——奇數堆和偶數堆,先開始的人可以分別計算這兩種堆的總和,如果奇數堆的和大,那麼就可以一直選擇奇數堆的石子堆(為什麼可以一直選擇奇數堆的石子呢?因為堆數為偶數,兩端的堆就分別屬於奇數堆和偶數堆,先開始的人拿走一堆後,兩端的堆就只屬於奇數堆或只屬於偶數堆了,後開始的人拿走一堆後,先開始的人又可以選擇拿奇數的堆或偶數的堆了),反之亦然,所以誰先開始,誰就能勝。這題Alex先開始,所以可以直接返回true。

用動態規劃可以解決更一般化的問題,如石子堆數不一定是偶數,不僅要判斷勝負,還要計算得分等等。那麼子問題是什麼呢?我們可以先計算出連續石子堆總數為1的勝負情況(Alex一定勝對吧!此時Alex最終比Lee多得到的石子數就等於這個石子堆的石子數,我們把它稱為分數),然後計算連續石子堆總數為2的情況(Alex同樣可以很輕鬆地選擇總數大的那一堆,Alex的分數也很容易計算),接著計算連續堆總數為3的情況(得思考一下了,Alex可以選擇第一堆或者第三堆,選完之後,就還剩下兩堆了,這時Lee同樣也會做出最佳選擇,也就是說,Lee的暫時得分可以通過前面計算出的,連續石子堆總數為2的情況得出!那麼Alex的得分,就等於Alex選出的那一堆的石子數,減去Lee的暫時得分),以此類推,可以算出連續堆總數為4、5、6……n-1的情況,最後就能算出連續堆總數為n的情況啦。

我們用dp[ i ][ j ]表示連續石子堆為原石子堆中第i堆到第j堆(包含i,j)時,先開始的人的得分。當只有一堆時,也就是i = j時,dp[ i ][ j ] 就等於piles[ i ],當有n堆時,dp[ i ][ j ]可以由n-1堆時的先手得分算出,選最左邊的堆時,得分為piles[ i ] - dp[ i + 1 ][ j ],選最右邊的堆時,得分為piles[ j ] - dp[ i ][ j - 1 ], dp[ i ][ j ] 取兩者中分數大者。

C++程式碼

class Solution {
public:
    bool stoneGame(vector<
int>& piles) { int l = piles.size(); int dp[l][l] = {}; for(int i = 0; i < l; i++) dp[i][i] = piles[i]; for(int n = 1; n < l; n++) for(int i = 0; i < l - n; i++) dp[i][i + n] = max(piles[i] - dp[i + 1][i + n], piles[i + n] -
dp[i][i + n - 1]); return dp[0][l - 1] > 0; } };