[Sicily 1176 Two Ends] 動態規劃 記憶化搜尋
阿新 • • 發佈:2019-02-18
(1)問題描述
題目的意思是:
給出一行個數為偶數的從左到右排列的卡片,每個卡片上寫有一個正整數。從玩家1開始,兩個玩家輪流從那一行卡片的最左端或是最右端取一張卡片。最後計算兩個玩家所得到的總分,總分大者為贏。玩家2使用的是貪心策略,也就是他每次都會取兩端中最大的那一個卡片。然而貪心策略並不總是最好的。現在的問題是:假設玩家1足夠聰明,在玩家2使用貪心策略的情況下,玩家1最多可以贏玩家2多少分。
(2)舉個例子
假設一個輸入樣例是這樣的:(第一個數代表卡片的個數為4,後面4個數為每個卡片的數值)
4 3 2 10 4
那麼對應的輸出應該是這樣的:
In game 1, the greedy strategy might lose by as many as 7 points.
為什麼是7 points?
玩家1選3 卡片為2 10 4
玩家2選2和4中最大的,選4 卡片為2 10
玩家1選10 卡片為2
玩家2選2 遊戲結束
玩家1得分3 + 10 = 13
玩家2得分4 + 2 = 6
玩家1 - 玩家2 = 7
(3)動態規劃(Dynamic Programming)
動態規劃的本質,是對問題狀態的定義和狀態轉移方程的定義。
a.要使用動態規劃解決這個問題,首先要思考狀態是什麼:
定義陣列dp[l][r],表示最左邊的卡片下標為為l,最右邊的卡片下標為r的時候,玩家1可以得到的最大的分數。
b.然後思考狀態轉移方程的構建:
dp[l][r]如何從前面的狀態轉移過來?
有兩種情況:
情況一:玩家1選的是左端的卡片
如果玩家1選完之後,左端的卡片比右端的卡片大,那麼玩家2選的是左端的卡片,玩家1得分 = card[l] + dp[l - 2][r]
如果玩家1選完之後,左端的卡片比右端的卡片小,那麼玩家2選的是右端的卡片,玩家1得分 = card[l] + dp[l + 1][r - 1]
情況二:玩家1選的是右端的卡片
如果玩家1選完之後,左端的卡片比右端的卡片大,那麼玩家2選的是左端的卡片,玩家1得分 = card[r] + dp[l + 1][r - 1]
如果玩家1選完之後,左端的卡片比右端的卡片小,那麼玩家2選的是右端的卡片,玩家1得分 = card[r] + dp[l][r - 2]
比較這兩種情況的得分,dp[l][r] = max(情況一得分,情況二得分);
(4)記憶化搜尋
這樣搜尋會有重複的時候,所以對於已經計算過的狀態不再計算,記憶化下來。
具體表現為初始化dp[i][j]為-1,如果後來搜尋發現已經被賦值,就直接返回。
(5)程式碼實現
#include<iostream>
using namespace std;
#define MAX 1001
int card[MAX];
int dp[MAX][MAX];
void init()
{
fill(card, card + MAX, -1);
for(int i = 0; i < MAX; i++)
fill(dp[i], dp[i] + MAX, -1);
}
int max_card(int l, int r)
{
return (card[l] > card[r] ? card[l]: card[r]);
}
int solve(int l, int r)
{
int t1 = 0;
int t2 = 0;
if(r - l == 1)
return max_card(l, r);
if(dp[l][r] != -1)
return dp[l][r];
else
{
if(card[l + 1] >= card[r])
t1 = card[l] + solve(l + 2, r);
else
t1 = card[l] + solve(l + 1, r - 1);
if(card[l] >= card[r - 1])
t2 = card[r] + solve(l + 1, r - 1);
else
t2 = card[r] + solve(l, r - 2);
}
dp[l][r] = (t1 > t2 ? t1: t2);
return dp[l][r];
}
int main()
{
int n;
int game_index = 1;
while(cin >> n && n != 0)
{
init();
int sum = 0;
for(int i = 0; i < n; i++)
{
cin >> card[i];
sum += card[i];
}
int player1 = solve(0, n - 1);
int player2 = sum - player1;
cout << "In game " << game_index << ", the greedy strategy might lose by as many as " << player1 - player2 << " points." << endl;
game_index++;
}
return 0;
}