LeetCode的石子遊戲系列

2021-10-21 07:28:13 字數 3576 閱讀 4206

整理一下亞歷克斯和李用兩個人閒來無聊玩的7場石子遊戲(╯‵□′)╯︵┻━┻

雙方博弈的題一般都是動態規劃解決。也不排除像第一場遊戲有討巧的解法,或者第6場用的貪心方法。

一般來講,兩端都可以取,考慮二維dp;只有一端,考慮一維dp;任意取,大概率貪心。

兩端的情況,推薦順序(遊戲7->遊戲5)

一端的情況,推薦順序(遊戲3->遊戲4->遊戲2)

對dp的設定有兩種,一種是對於這部分石子,當前選手能拿到的最高分數,另一種是對於這部分石子,當前選手能拿到的最大差值。一般設為最大差值可以減少一些不必要的運算。

這裡講一下為什麼用差值比較好

假設當前選手a取i位置的石子

最大差值

dp[i] = 新增的分數-dp[i+1]

解釋dp[i+1]表示下乙個選手b取得最大差值 = b-a

新增的分數-(b-a) = a0+a-b = 當前選手a能得到的最大差值

最高分數

dp[i] = 新增的分數+(dp[i+1]之後能給雙方選手帶來的最大分數-dp[i+1])

解釋dp[i+1]表示下乙個選手b取得最大分數 = b

新增的分數+(dp[i+1]之後能給雙方選手帶來的分數之和-dp[i+1]) = a0+(a+b-b) = a0+a=當前選手a能得到的最高分數

很明顯可以看到對於dp[i+1]之後能給雙方選手帶來的分數之和若是條件稍微變一下,是不太容易求的。

本題用動態規劃肯定是可以解的,但是本題有幾個限制,讓這題有了更簡單的解法。一則,石子為奇數(肯定有勝負);二則,只需要判斷勝負(不需要找到最優解),三則,石子堆數為偶數(每人拿的堆數都一樣)。

這麼解釋,把一堆石子按奇偶數分,奇數字置的和,偶數字置的和,兩者肯定有乙個更大的,作為先手的話,他可以選取大一些的那個。

比如說,先手a想拿偶數字置的,只要上來拿0編號,對於後手b,只能拿1編號和n-1編號(均為奇數),然後b任意拿乙個,肯定會暴露出乙個偶數編號,a繼續拿這個偶數編號的。

所以說先手總能選大一些的那一堆,即肯定會贏。

class

solution

};

// dp[i][j]表示剩餘[i : len - 1]堆時,m = j的情況下,先取的人能獲得的最多石子數

// dp[i][m]=max

class

solution}}

return dp[0]

[1];

}};

這個是最經典的石子遊戲的動態規劃解決辦法。

對於當前選手來說,我有三種取法,那當前選手選擇的應該是能讓他與下乙個選手差值最大的。

//dp[i]: 對於石子stones[i..n-1]序列,當前選手可以取到的最大差值

//dp[i] = max(

// stone[i]-dp[i+1],

// stone[i]+stone[i+1]-dp[i+2],

// stone[i]+stone[i+1]+stone[i+2]-dp[i+3],

//)class

solution

}return dp[0]

==0?"tie"

: dp[0]

>0?

"alice"

:"bob";}

};

這題石子沒有分數,其實相當於也是從一端開始拿,也是一位dp。不難想象對於石子數為n,當前選手有k種拿法,k

2<=n

,kϵ[

1,k]

k^2<=n, k\epsilon[1,k]

k2<=n

,kϵ[

1,k]

,那當前選手想贏的話,只要有乙個k滿足dp[n-k*k]必輸,那當前選手就必贏。

//dp[i]: i個石子當前選手的輸贏狀態

class

solution

}return dp[n];}

};

本題也是二維dp的問題,你可以把他理解成從左端或者從右端拿掉一堆石子的問題。

只不過這裡用乙個要遍歷k(分隔線),至於是拿k的左半部分,還是k的右半部分就取決於左半部分的和相比右半部分的和哪個更大了。計算一段序列的和可以考慮用字首和陣列。

不過本題其實可以優化一下,複雜度降為n^2,這裡暫時不討論。

/*

dp[i][j]: 對於石子stone[i...j],alice能得到的最大分數

在[i+1,j] 中遍歷k,找到分數最大的那個作為dp[i][j]

對於分隔線k,將[i...j]分隔為[i..k-1]和[k...j]

當sum[i...k-1]>sum[k...j] 分數為sum[k...j]+dp[k][j]

當sum[i...k-1]class

solution

;int dp[

505]

[505];

for(

int i =

1; i <= n;

++ i)

pre[i]

+= pre[i-1]

+ stonevalue[i-1]

;for

(int i = n-

2; i >=0;

-- i)}}

return dp[0]

[n-1];

}};

本題是貪心解法。

假設有兩個石子,對a來說價值是a1, a2,對b來說價值是b1, b2。

若a取1,價值差為a1-b2

若a取2,價值差為a2-b1

那取哪個只需要判斷a1-b2 與a2-b1的大小,移項後即只要比較a1+b1與a2+b2的大小。

對先手來說,盡可能取這個石頭總價值高的。

整個思路就有了,把石頭按總價值排序,依次取就可以。

class

solution);

sort

(stones.

begin()

, stones.

end(),

(vector<

int>

& a, vector<

int>

& b));

int alicesum =

0, bobsum =0;

for(

int i =

0; i < n;

++ i)

return alicesum == bobsum ?0:

(alicesum > bobsum ?1:

-1);

}};

兩端都可以取,二維dp;用到一段序列的和,字首和陣列。

/*

dp[i][j]:對於石子序列stone[i...j],當前選手能獲得的最大分數差值

對於dp[i][j]

當前選手有兩種取法

sum[i+1...j]-dp[i+1][j]

sum[i...j-1]-dp[i][j-1]

*/class

solution}}

return dp[0]

[n-1];

}};

取石子遊戲系列(1)

題目來自 程式設計之美 一排石頭的遊戲 n塊石頭排成一列,每塊石頭都有自己的固定位置,也就是相當於有自己的編號一樣。兩個玩家依次取石頭,每個玩家每次可以取其中任意一塊石頭,或者相鄰的兩塊,最後將所有石頭取走的玩家贏。這個遊戲有必勝策略嗎?取石子有很多變種,限定取石子的規則,就能產生不同的玩法。這類遊...

LeetCode 石子遊戲(動態規劃)

亞歷克斯和李用幾堆石子在做遊戲。偶數堆石子排成一行,每堆都有正整數顆石子 piles i 遊戲以誰手中的石子最多來決出勝負。石子的總數是奇數,所以沒有平局。亞歷克斯和李輪流進行,亞歷克斯先開始。每回合,玩家從行的開始或結束處取走整堆石頭。這種情況一直持續到沒有更多的石子堆為止,此時手中石子最多的玩家...

LeetCode877 石子遊戲

亞歷克斯和李用幾堆石子在做遊戲。偶數堆石子排成一行,每堆都有正整數顆石子 piles i 遊戲以誰手中的石子最多來決出勝負。石子的總數是奇數,所以沒有平局。亞歷克斯和李輪流進行,亞歷克斯先開始。每回合,玩家從行的開始或結束處取走整堆石頭。這種情況一直持續到沒有更多的石子堆為止,此時手中石子最多的玩家...