leetcode史上最屌解法

2021-10-05 10:14:27 字數 3103 閱讀 8504

題目大意如下所描述:我們有一對好**alex和lee在玩雙人♂遊戲,遊戲規則如下所述:

亞歷克斯和李用幾堆石子在做遊戲。偶數堆石子排成一行,每堆都有正整數顆石子 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 先選,兩個人都會一直做最優選擇,問我們最終 alex 是否能獲勝。這一題可以用動態規劃來做,還有一種讓人虎軀一震的做法,堪稱leetcode有史以來最簡單的**。

動態規劃的做法,還是老規矩,動態規劃三部曲:

由於玩家獲勝的規則是拿到的石子數多,所以我們用乙個二維陣列,其中dp[i][j]表示在區間[i, j]內先手拿石子,在理想策略下,可以多拿的石子數,若為正數,說明先手拿得可以必勝,若為負數,則表示先手拿必輸。

則最終只要看dp[0][n-1]的值,若為正數,則alex先手拿能獲勝。

為什麼要這麼定義dp陣列?請接著看下文。

假設我們現在處理的子問題是dp[i][j],我們現在要計算這個時候先手拿能多拿的石子數。在這個區間上,先手拿的人只能拿i位置或者j位置的石子,假如先手的人拿了piles[i]的話,等於先手的人多了piles[i]個石子。這個時候,區間縮小了,變成了[i+1,j],這個時候,另乙個人變成了先手的人,問題變成了dp[i+1][j]。假設我們已經知道了dp[i+1][j]的值,也就是此時後手(次回合先手)的人能多拿的石子個數,所以,先手的人若選擇了piles[i],那麼他能多拿的石子淨個數為piles[i] - dp[i + 1][j].

同樣的道理,如果[i,j]時先手的那個人選了j,那麼他能多拿的石子淨個數為piles[j] - dp[i][j - 1]。

最後我們需要在二者中取最大值,就是此時dp[i][j]的值

dp[i]

[j]=

max(piles[i]

- dp[i +1]

[j], piles[j]

- dp[i]

[j -1]

)

始化只有i乙個石頭堆的情形,初始化只需要考慮有乙個石頭堆的情況,很簡單:

for i in

range

(n):

dp[i]

[i]= piles[i]

可以直接在leetcode上提交的**:

class

solution

:def

stonegame

(self, piles: list[

int])-

>

bool

: n =

len(piles)

dp =[[

0]*n for _ in

range

(n)]

for i in

range

(n):

dp[i]

[i]= piles[i]

for i in

range

(n-1):

for j in

range

(n):

dp[i]

[j]=

max(piles[i]

-dp[i+1]

[j], piles[j]

- dp[i]

[j -1]

)return dp[0]

[n-1

]>

0

以上是常規做法,老實刷題的小白專屬。

這一題是leetcode周賽出過的一道題,最快的一位同學,也是一位著名大神,來自日本的uwi選手,只用了一分鐘多一點就做出了這題。他是怎麼做到的?

我們來看一下他的**(改寫成python):

def

stonegame

(self, piles: list[

int])-

>

bool

:return

true

只有一行?!

是的,你沒有看錯!leetcode已經水到了medium難度的題目一行就能寫完的地步(誤)。

事實就是:先走的人一定可以必勝!

其實這道題,是一道腦筋急轉彎題,我們分析一下:

因為題目給了限定條件,總和是奇數,堆的個數是偶數。我們按照奇偶分成兩堆,因為總和為奇數,所以這兩堆的石子總和一定不相同。那麼alex只要先手算出奇數堆裡的石子多還是偶數堆裡的石子多,那麼一定必勝。

比如分成兩堆後,偶數堆裡面石子的總和比較多,alex選擇偶數,piles[0], piles[2], …, piles[n-2],他選擇了piles[0],這個時候lee可以選擇piles[1] 或 piles[n - 1].之後alex可以繼續選擇偶數的位置。所以lee就被迫選擇了所有奇數的位置。

反之,如果alex從倒數第乙個開始選,那麼他能選到所有的奇數字置,lee被迫選偶數字置。故,alex只要選出奇數、偶數字置中求和之後最大的就行,一定會贏。

這一題應該算是leetcode裡面比較奇葩的一題了。如果做的時候能瞬間想通這個腦筋急轉彎,那這題就是秒殺。如果一時半會沒有想到這個,老實用動態規劃做,也可以比較輕鬆的做出來。

史上最牛的面試

1.你為什麼來應聘這份工作。答 以前俺是乙隻迷途的騾子,現在可算找到組織了。2.你是怎麼知道我們招聘這個職位的呢?答 乙個合格的員工除了要有騾子般的身體以外,還必須有獵狗一樣的嗅覺。3.我們為什麼要聘你呢?答 俺吃的少,拉的多。4.你認為自己最大的優點是什麼?答 像騾子一樣吃苦,像工蜂一樣耐勞,像獵...

史上最經典的黑客

是我當黑客以來接到的一項最具挑戰性的生意,是去黑另乙個黑客的電腦。工欲善其事,必先利其器,我很明白現今裝備的重要性,於是我把自己從頭武裝到 腳,包裝上了全黑的緊身夜行衣和戴上墨鏡。這樣我看起來就很黑很客,很重很要了。一出門,我就給車撞了 司機下車後,打著強光手電筒找了好半天才找到了躺在地上的我,說了...

史上最經典的黑客

是我當黑客以來接到的一項最具挑戰性的生意,是去黑另乙個黑客的電腦。工欲善其事,必先利其器,我很明白現今裝備的重要性,於是我把自己從頭武裝到 腳,包裝上了全黑的緊身夜行衣和戴上墨鏡。這樣我看起來就很黑很客,很重很要了。一出門,我就給車撞了 司機下車後,打著強光手電筒找了好半天才找到了躺在地上的我,說了...