整理一下亞歷克斯和李用兩個人閒來無聊玩的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 遊戲以誰手中的石子最多來決出勝負。石子的總數是奇數,所以沒有平局。亞歷克斯和李輪流進行,亞歷克斯先開始。每回合,玩家從行的開始或結束處取走整堆石頭。這種情況一直持續到沒有更多的石子堆為止,此時手中石子最多的玩家...