日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Nim 游戏 、⽯头游戏1、石头游戏2

發布時間:2024/4/11 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Nim 游戏 、⽯头游戏1、石头游戏2 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Nim 游戲 、?頭游戲1、石頭游戲2

文章目錄

  • Nim 游戲 、?頭游戲1、石頭游戲2
    • **一:Nim 游戲**
    • **二:?頭游戲**
    • **三、石頭游戲2**
      • **方法一:DP 函數**
      • **方法二:DP table**

一:Nim 游戲

你和你的朋友,兩個人一起玩 Nim 游戲:桌子上有一堆石頭,每次你們輪流拿掉 】1 - 3 塊石頭。 拿掉最后一塊石頭的人就是獲勝者。你作為先手。你們是聰明人,每一步都是最優解。 編寫一個函數,來判斷你是否可以在給定石頭數量】的情況下贏得游戲。

示例:

輸入: 4 輸出: false 解釋: 如果堆中有 4 塊石頭,那么你永遠不會贏得比賽;因為無論你拿走 1 塊、2 塊 還是 3 塊石頭,最后一塊石頭總是會被你的朋友拿走

解題思路:

我們解決這種問題的思路?般都是反著思考:

1 . 如果我能贏, 那么最后輪到我取??的時候必須要剩下 1~3 顆??, 這樣 我才能?把拿完。

2.如何營造這樣的?個局?呢? 顯然, 如果對?拿的時候只剩 4 顆??, 那么?論他怎么拿, 總會剩下 1~3 顆??, 我就能贏。

3.如何逼迫對??對 4 顆??呢? 要想辦法, 讓我選擇的時候還有 5~7 顆??, 這樣的話我就有把握讓對?不得不?對 4 顆??。

4.如何營造 5 ~ 7 顆??的局?呢? 讓對??對 8 顆??, ?論他怎么拿, 都會給我剩下 5~7 顆, 我就能贏。

5.這樣?直循環下去, 我們發現只要踩到 4 的倍數, 就落?了圈套, 永遠逃不出 4 的倍數, ?且?定會輸。 所以這道題的解法?常簡單:

bool canWinNim(int n) { // 如果上來就踩到 4 的倍數, 那就認輸吧 // 否則, 可以把對?控制在 4 的倍數, 必勝 return n % 4 != 0; }

二:?頭游戲

亞歷克斯和李用幾堆石子在做游戲。偶數堆石子排成一行,每堆都有正整數顆石子 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) 是奇數。
  • 強調雙?都很聰明的原因, 算法也是求最優決策過程下你是否能贏。這道題?涉及到兩?的博弈, 也可以?動態規劃算法暴?試, ?較?煩。 但我們只要對規則深?思考, 就會?驚失?: 只要你?夠聰明, 你是必勝?疑的, 因為你是先?。

    boolean stoneGame(int[] piles) {return true; }

    這是為什么呢, 因為題?有兩個條件很重要: ?是?頭總共有偶數堆, ?頭的總數是奇數

    這兩個看似增加游戲公平性的條件, 反?使該游戲成為了?個割?菜游戲。

    我們以 piles=[2, 1, 9, 5] 講解, 假設這四堆?頭從左到
    右的索引分別是 1, 2, 3, 4。

    • 如果我們把這四堆?頭按索引的奇偶分為兩組, 即第 1、 3 堆和第 2、 4 堆,
    • 那么這兩組?頭的數量?定不同, 也就是說?堆多?堆少。 因為?頭的總數是奇數, 不能被平分。
    • ?作為第?個拿?頭的?, 你可以控制??拿到所有偶數堆, 或者所有的奇數堆。
    • 你最開始可以選擇第 1 堆或第 4 堆。 如果你想要偶數堆, 你就拿第 4 堆, 這樣留給對?的選擇只有第 1、 3 堆, 他不管怎么拿, 第 2 堆?會暴露出來,你就可以拿。
    • 同理, 如果你想拿奇數堆, 你就拿第 1 堆, 留給對?的只有第2、 4 堆, 他不管怎么拿, 第 3 堆?給你暴露出來了。
    • 也就是說, 你可以在第?步就觀察好, 奇數堆的?頭總數多, 還是偶數堆的?頭總數多, 然后步步為營, 就?切盡在掌控之中了。

    三、石頭游戲2

    亞歷克斯和李繼續他們的石子游戲。許多堆石子 排成一行,每堆都有正整數顆石子 piles[i]。游戲以誰手中的石子最多來決出勝負。

    亞歷克斯和李輪流進行,亞歷克斯先開始。最初,M = 1。

    在每個玩家的回合中,該玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。

    游戲一直持續到所有石子都被拿走。

    假設亞歷克斯和李都發揮出最佳水平,返回亞歷克斯可以得到的最大數量的石頭。

    示例:

    輸入:piles = [2,7,9,4,4] 輸出:10 解釋: 如果亞歷克斯在開始時拿走一堆石子,李拿走兩堆,接著亞歷克斯也拿走兩堆。在這種情況下,亞歷克斯可以拿到 2 + 4 + 4 = 10 顆石子。 如果亞歷克斯在開始時拿走兩堆石子,那么李就可以拿走剩下全部三堆石子。在這種情況下,亞歷克斯可以拿到 2 + 7 = 9 顆石子。 所以我們返回更大的 10

    提示:

    • 1 <= piles.length <= 100
    • 1 <= piles[i] <= 10 ^ 4

    解題思路:

    定義

    • stones:與題目中 piles 意思保持一致;
    • n:stone個數;
    • score[i, j]:當前玩家從 stones[i] 開始,且 M = j 時,該玩家所能得到的最高分數;
    • sum[i]:從stones[i]開始,剩下石頭分數之和(即,stones[i] + … + stones[n - 1]);

    考慮

    • 如何使得當前玩家所能獲得的分數score[i, j]最高?取1個?取2個?… 取 2 * j 個?
    • 當前玩家拿1個stone時,另一個玩家接下來所能獲得的最高分數為score[i + 1, max(j, 1)]
    • 當前玩家拿2個stone時,另一個玩家接下來所能獲得的最高分數為score[i + 2, max(j, 2)];
    • 當前玩家拿2*j個stone時,另一個玩家接下來所能獲得的最高分數為score[i + 2 * j, max(j, 2 * j)];
    • 因為sum[i]是一定的,只需要使另一個玩家接下來所能獲得的最高分數最小,即可保證當前玩家所得到的分數score[i, j]最大

    基于上述考慮,得到遞歸方程如下:

    score[i,j]=sum[i] ? min(socre[i + 1,max(j,1)],...,score[i + 2 ? j,max(j,2 ? j)])

    進一步考慮,遞歸終止條件:

    因為所有stone的分數都為正數;當 n - i <= 2 * j 時(即,當前玩家可以直接拿下剩下的所有stone),則當前玩家所得最高分 score[i, j] 直接為剩下所有stone分數之和(即,score[i, j] = sum[i]);

    完整遞歸方程如下:

    回到題目

    • 一場游戲,Alex所能獲得的最高分為score[0,1];
    • 對應的,Lee所獲得的分數為 sum[0]?score[0,1];
    • 誰獲得的分數高,誰贏;

    為了可以在常數時間內計算到sum[i],我們需要提前計算好后綴和數組;

    當n = 8時,示例如下:

    正如前面所說,當 2 * j > n - i 時,當前玩家可以拿走剩下的所有stone,因此這里 j 的上限,我們取 n 的一半向上取整;

    為了計算score[0, 1],我們需要知道score[1, 1],score[2, 2],所以計算時我們采用從下到上(從左到右或者從右到左都行)的計算順序;

    方法一:DP 函數

    class Solution { public:int n = 0;//表示從i開始取M個獲取的最大價值int* vec;//備忘錄int mem[105][105];//dp函數int dp(int s, int M) {//如果在備忘錄中直接在備忘錄中返回,避免重復計算if(mem[s][M] != 0) return mem[s][M];//如果s >= n代表下標已經越界,相當于piles越界if(s >= n) return 0;//如果發現 s + 2 * M >= n 說明玩家可以一次取完,直接返回if(s + 2 * M >= n){return vec[s];}int ans = 0;for(int i = 1; i <= 2 * M; i++) {//當前的最大價值為,當前剩余價值 - 下一個狀態的最大價值。ans = max(ans, vec[s] - dp(s + i, max(i, M))); }//記憶化搜索mem[s][M] = ans;return ans;}int stoneGameII(vector<int>& piles) {n = piles.size();//初始化相關操作memset(mem, 0, sizeof(mem));vec = new int[n];//從 后往前計算每個 i 位置前 的所有石頭之和int sum = 0;for(int i = n - 1; i >= 0; i--) {sum += piles[i];vec[i] = sum;}//遞歸調用dp函數求結果,0:代表玩家從piles的0下標處開始可以獲得的最大石頭數量 ;1:代表m的初始值為1return dp(0,1);} };

    方法二:DP table

    class Solution { public:int stoneGameII(vector<int>& piles) {int len = piles.size();//用來從前往后計算每個 i 位置的所有石頭之和int sum = 0; // dp[i][j]表示當前是第i波,m = j,玩家所能獲得的最大石頭的數量;vector<vector<int>>dp(len + 1, vector<int>(len + 1, 0));//注意要反向遍歷for(int i = len - 1; i >= 0; i--){sum += piles[i];// 表示當前所剩下的所有棋子的和for(int M = 1; M <= len; M++){//代表玩家可以一次取完剩下的所有石頭,直接放到dp中,繼續循環測試if(i + 2 * M >= len){//直接把剩下的所有石頭保存在dp數組中dp[i][M] = sum;continue;}//用循環來枚舉所有的情況,取其中玩家可以獲得最大石頭數量的一個結果保存//x代表每次取的堆數//i + x <= len 是為了防止越界//x <= 2 * M 代表每次可以取的石頭的堆數范圍for(int x = 1; i + x <= len && x <= 2 * M; x++){//每次選取其中結果最大 的來保存dp[i][M] = max(dp[i][M], sum - dp[i + x][max(M, x)]);}}}return dp[0][1];} };

    總結

    以上是生活随笔為你收集整理的Nim 游戏 、⽯头游戏1、石头游戏2的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。