1. 題目來源
2. 題目說明
3. 題目解析
方法一:博弈dp+記憶化+巧妙解法
依稀記得在專業課《運籌學》上學習過 博弈論 相關知識。
由於 Alice
是先取的,且都以 最優策略 取石子,那麼就會產生三種情況,即:Alice
第一次 取 1 堆、取 2 堆、取 3 堆分別對應三個結果,那麼分別對比這三個情況的結果即可。代碼已經過詳細註釋,提供非註釋版。
雖然大佬講解的很細緻,但是 dp
、dfs
基礎還是很薄弱,導致聽得有點迷糊,索性拿着代碼進行單步調試就理解了。主要是一個 深搜+記憶化 的過程,將所有狀態全部計算出來。用到深搜肯定離不開回溯,一開始沒明白爲啥 dp
數組存放的是 Alice - Bob
的得分,卻還要將 Alice
的 cur
加上 dfs
的下一個值。其實這就是 dfs
的特性了,當 dfs
越界後返回 0,那麼 Alice
從後向前回溯就是最後一堆石子的得分,此時 Bob
沒選擇,所以 Alice
得正分,同理 Bob
得負分。
開始回溯時,即前半段都是兩者取 1 堆的情況,後面就是取 2 堆、取 3 堆進行一個最大值、最小值的比較。確實很抽象。
真的是理解不了的,進行一個簡單的單步調試,中間輸出 num
一些中間參數,就能很容易理解這個思想了。
其實理解了這個博弈思想,是個很簡單的題目,可惜…建議把石子合併前幾道系列題做了,理解下簡單的動規、博弈思想會好很多。大佬們都評價這道題難度只能算個 Medium
。
確實我的理解也不到位,也有大佬很簡潔的一個 dp
方程就搞定了,我還是先把前導題啃了再理解理解吧。希望我上面的個人理解對讀者有所幫助…
參見代碼如下:
// 執行用時 :1196 ms, 在所有 C++ 提交中擊敗了100.00%的用戶
// 內存消耗 :143.2 MB, 在所有 C++ 提交中擊敗了100.00%的用戶
const int MAXN = 50000 + 50;
bool vis[MAXN][2];
// dp數組,dp[i][k]表示當前取到第i堆石子,當前爲第k個人取,k=0表示Alice,k=1表示Bob
// dp數組存放值爲當前Alice分數減去Bob分數,Alice取石子即最大化dp值,Bob取石子即最下化dp值即可
int dp[MAXN][2]; // Alice - Bob
vector<int> num;
// 深搜
// 當前取到第x堆石子,共有n堆石子,輪到誰取
int dfs(int x, int n, int turn) {
if (vis[x][turn]) return dp[x][turn];
if (x >= n) return 0;
vis[x][turn] = true;
// Alice取
if (turn == 0) {
// 讓dp數組先存取一堆的情況,Alice取x堆的石子,即num[x],Bob就需要從x+1堆開始取了
int cur = num[x];
// 在此理解cur + dfs(x + 1, n, turn ^ 1);就是深搜,深搜至最後確定最後一個狀態,dfs(x + 1, n, turn ^ 1)越界後爲0
// 若最後一個爲Alice取,則當前Alice爲正值,否則Bob爲負值
dp[x][turn] = cur + dfs(x + 1, n, turn ^ 1);
for (int i = x + 1; i < n && i < x + 3; ++i) {
cur += num[i];
// Alice需要最大化dp值,dp值爲Alice減Bob的分數
dp[x][turn] = max(dp[x][turn], cur + dfs(i + 1, n, turn ^ 1));
}
}
else {
// Bob也是先取一堆石子
int cur = num[x];
// Bob分數爲Alice取下一堆石子的分數減去Bob的當前取的分數,因爲dp數組爲Alice-Bob的分數
dp[x][turn] = dfs(x + 1, n, turn ^ 1) - cur;
for (int i = x + 1; i < n && i < x + 3; ++i) {
cur += num[i];
dp[x][turn] = min(dp[x][turn], dfs(i + 1, n, turn ^ 1) - cur);
}
}
return dp[x][turn];
}
class Solution {
public:
string stoneGameIII(vector<int>& stoneValue) {
int n = stoneValue.size();
// 初始化
// vis表示該變量是否已經被記憶化
// dp表示該變量的值
for (int i = 0; i <= n; ++i)
for (int k = 0; k < 2; k++)
dp[i][k] = 0, vis[i][k] = false;
num = stoneValue;
int ans = dfs(0, n, 0);
if (ans > 0) return "Alice";
if (ans < 0) return "Bob";
return "Tie";
}
};
參見代碼如下:
const int MAXN = 50000+ 50;
bool vis[MAXN][2];
int dp[MAXN][2]; // Alice - Bob
vector<int> num;
int dfs(int x, int n, int turn){
if (vis[x][turn]) return dp[x][turn];
if (x >= n) return 0;
vis[x][turn] = true;
if (turn == 0){
int cur = num[x];
dp[x][turn] = cur + dfs(x + 1, n, turn ^ 1);
for (int i = x + 1; i < n && i < x + 3; i++){
cur += num[i];
dp[x][turn] = max(dp[x][turn], cur + dfs(i + 1, n, turn ^ 1));
}
}else{
int cur = num[x];
dp[x][turn] = dfs(x + 1, n, turn ^ 1) - cur;
for (int i = x + 1; i < n && i < x + 3; i++){
cur += num[i];
dp[x][turn] = min(dp[x][turn], dfs(i + 1, n, turn ^ 1) - cur);
}
}
return dp[x][turn];
}
class Solution {
public:
string stoneGameIII(vector<int>& stoneValue) {
int n = stoneValue.size();
for (int i = 0; i <= n; i++)
for (int k = 0; k < 2; k++)
dp[i][k] = 0, vis[i][k] = false;
num = stoneValue;
int ans = dfs(0, n, 0);
if (ans > 0) return "Alice";
if (ans < 0) return "Bob";
return "Tie";
}
};