1. 程式人生 > >Stone Game

Stone Game

ber tails con article mos i+1 提示 依次 輸入

Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i].

The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties.

Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins.

Assuming Alex and Lee play optimally, return True if and only if Alex wins the game.

Example 1:

Input: [5,3,4,5]
Output: true
Explanation: 
Alex starts first, and can only take the first 5 or the last 5.
Say he takes the first 5, so that the row becomes [3, 4, 5].
If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points.
If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points.
This demonstrated that taking the first 5 was a winning move for Alex, so we return true.

Note:

  1. 2 <= piles.length <= 500
  2. piles.length is even.
  3. 1 <= piles[i] <= 500
  4. sum(piles) is odd.

877. 石子遊戲
亞歷克斯和李用幾堆石子在做遊戲。偶數堆石子排成一行,每堆都有正整數顆石子 piles[i] 。

遊戲以誰手中的石子最多來決出勝負。石子的總數是奇數,所以沒有平局。

亞歷克斯和李輪流進行,亞歷克斯先開始。 每回合,玩家從行的開始或結束處取走整堆石頭。 這種情況一直持續到沒有更多的石子堆為止,此時手中石子最多的玩家獲勝。

假設亞歷克斯和李都發揮出最佳水平,當亞歷克斯贏得比賽時返回 true ,當李贏得比賽時返回 false 。

示例:
輸入:[5,3,4,5]
輸出:true
解釋:
亞歷克斯先開始,只能拿前 5 顆或後 5 顆石子 。
假設他取了前 5 顆,這一行就變成了 [3,4,5] 。
如果李拿走前 3 顆,那麽剩下的是 [4,5],亞歷克斯拿走後 5 顆贏得 10 分。
如果李拿走後 5 顆,那麽剩下的是 [3,4],亞歷克斯拿走後 4 顆贏得 9 分。
這表明,取前 5 顆石子對亞歷克斯來說是一個勝利的舉動,所以我們返回 true 。

提示:
2 <= piles.length <= 500
piles.length 是偶數。
1 <= piles[i] <= 500
sum(piles) 是奇數。
題解:
由於題目的限制條件是石頭的堆數是偶數,且石頭的總數是奇數,因此Alex可以選擇一種策略總是選偶數堆或者奇數堆的石頭,則一定可以勝過Lee。簡單說,Alex在題目的條件限制下是必勝的。但這裏我們需要進行更一般化的分析,例如石頭堆數不一定是偶數,石頭總數也不一定是奇數,且不但要判斷Alex是否能贏,還要判斷最多贏多少分,如果輸,能不能提供最少輸多少分。這裏的分數是指多拿的石頭數量。

我們每次只能拿兩端的石頭堆的石頭,但我們又不知道拿完後剩下的石頭堆的情況,因此我們考慮先解決子問題。例如我們求出2個相鄰石頭堆的勝負情況,我們可以根據求出的數據求出相鄰3個石頭堆的勝負情況,以此類推,我們可以根據n-1個相鄰石頭堆的勝負情況,求出n個相鄰石頭堆的勝負情況,即我們的原問題。
根據我們的類推我們可以設dp[i][j]為piles[i]~piles[j]Alex最多可以贏Lee的分數。每次取石頭堆只能從兩端取,因此:dp[i][j] = max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1])。其中
piles[i] - dp[i+1][j]表示Alex取走i上的石頭堆,piles[j] - dp[i][j-1]表示Alex取走的是j上的石頭堆。註意,為什麽dp[i+1][j]表示piles[i+1]~piles[j]之間Alex最多可以贏Lee的分數,而piles[i]要減去該值而不是加上該值呢?由於我們的要求是每一步Alex和Lee采取的都是最優策略,當取piles[i]時,piles[i+1]~piles[j]中Alex和Lee的走法會調換。意即Lee走Alex的走法,Alex走Lee的走法,因此這裏要做減法。

以題目中的[5, 3, 4, 5]為例,下圖是我們的計算步驟:


按照這個思路,很容易寫出完整的代碼:

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        int n = piles.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for(int i = 0; i < n; i++) {
            dp[i][i] = piles[i]; //初始化只有i一個石頭堆的情形
        }
        for(int dis = 1; dis < n; dis++) {//依次計算相鄰2個石頭堆到n個石頭堆的情形
            for(int i = 0; i < n - dis; i++) {
                dp[i][i+dis] = max(piles[i]-dp[i+1][i+dis], piles[i+dis]-dp[i][i+dis-1]);
            }
        }
        return dp[0][n-1] > 0;
    }
};


reference:https://blog.csdn.net/androidchanhao/article/details/81271077

Stone Game