考虑一行n个硬币,其值为v1。 。 。 vn,其中n为偶数。我们通过交替轮流与对手进行比赛。在每个回合中,玩家从该行中选择第一个或最后一个硬币,将其从该行中永久删除,然后接收硬币的价值。确定如果我们先走,我们绝对可以赢得的最大金额。
另外,在最佳游戏中打印移动顺序。由于许多移动顺序可能会导致最佳答案,因此您可以打印任何有效的顺序。
在序列之前,这些文章中已经讨论了该部分。
- 游戏的最佳策略
- 游戏的最佳策略|套装2
例子:
Input: 10 80 90 30
Output: 110 RRRL
Explanation:
P1 picks 30, P2 picks 90, P1 picks 80 and finally P2 picks 10.
Score obtained by P1 is 80 + 30 = 110
Max possible score for player 1 is : 110
Optimal game moves are : RRRL
Input: 10 100 10
Output: 20 RRL
方法:
在每个回合中(除了最后一个回合),玩家将有两个选择,要么是在行的左边捡起袋子,要么是在行的右边捡起袋子。我们的重点是评估P1可以达到的最高得分,以S为准。P1希望依次选择最高得分,而P2则希望为P1选择最低得分。
因此,P1专注于使S最大化,而P2专注于使S最小化。
天真的方法:
- 我们可以为该问题编写蛮力递归解决方案,该解决方案可以模拟游戏的所有可能性,并找到在给定约束下得分最高的分数。
- 函数maxScore返回玩家1的最大可能分数,以及游戏过程中发生的移动。
- 由于函数需要同时返回最大可能的得分和导致该得分的移动,因此我们使用一对integer和字符串。
- 代表游戏动作的字符串由字符“ L”和“ R”组成,意思是分别拾取了最左边或最右边的包。
下面是上述方法的实现:
// C++ implementation
#include
using namespace std;
// Calculates the maximum score
// possible for P1 If only the
// bags from beg to ed were available
pair maxScore(vector money,
int beg,
int ed)
{
// Length of the game
int totalTurns = money.size();
// Which turn is being played
int turnsTillNow = beg
+ ((totalTurns - 1) - ed);
// Base case i.e last turn
if (beg == ed) {
// if it is P1's turn
if (turnsTillNow % 2 == 0)
return { money[beg], "L" };
// if P2's turn
else
return { 0, "L" };
}
// Player picks money from
// the left end
pair scoreOne
= maxScore(money,
beg + 1,
ed);
// Player picks money from
// the right end
pair scoreTwo
= maxScore(money, beg, ed - 1);
if (turnsTillNow % 2 == 0) {
// if it is player 1's turn then
// current picked score added to
// his total. choose maximum of
// the two scores as P1 tries to
// maximize his score.
if (money[beg] + scoreOne.first
> money[ed] + scoreTwo.first) {
return { money[beg] + scoreOne.first,
"L" + scoreOne.second };
}
else
return { money[ed] + scoreTwo.first,
"R" + scoreTwo.second };
}
// if player 2's turn don't add
// current picked bag score to
// total. choose minimum of the
// two scores as P2 tries to
// minimize P1's score.
else {
if (scoreOne.first < scoreTwo.first)
return { scoreOne.first,
"L" + scoreOne.second };
else
return { scoreTwo.first,
"R" + scoreTwo.second };
}
}
// Driver Code
int main()
{
// Input array
int ar[] = { 10, 80, 90, 30 };
int arraySize = sizeof(ar) / sizeof(int);
vector bags(ar, ar + arraySize);
// Function Calling
pair ans
= maxScore(bags,
0,
bags.size() - 1);
cout << ans.first << " " << ans.second << endl;
return 0;
}
110 RRRL
上述方法的时间复杂度是指数的。
最佳方法:
我们可以通过在时间和空间的复杂性。
- 如果我们为所有从i开始到j结束的袋子存储最好的答案,那么最多可以有这样的子问题。
- 令dp(i,j)代表如果行中仅有的剩余袋子从i开始并在j结束时可以达到的最大分数P1 。然后以下内容成立:
- 如果轮到P1
- dp(i,j) =袋i + dp(i + 1,j)或袋j + dp(i,j-1)的最大分数。
- 如果轮到P2
- dp(i,j) = dp(i + 1,j)或dp(i,j-1)的最小值。
由于当前书包的得分为P2,因此我们不将其添加到dp(i,j) 。
- dp(i,j) = dp(i + 1,j)或dp(i,j-1)的最小值。
- 如果轮到P1
- 为了跟踪在给定状态下发生的移动,我们保留一个附加的布尔矩阵,该矩阵使我们能够重构导致最高得分的整个游戏移动。
- 矩阵leftBag(i,j)表示仅存在从i到j的袋子的状态。如果最佳选择leftBag,则leftBag(i,j)为1 ,否则为0 。
- 函数getMoves使用此矩阵来重建正确的移动。
下面是上述方法的实现:
// C++ implementation
#include
#define maxSize 3000
using namespace std;
// dp(i, j) is the best
// score possible if only
// the bags from i, j were
// present.
int dp[maxSize][maxSize];
// leftBag(i, j) is 1 if in the
// optimal game the player picks
// the leftmost bag when only the
// bags from i to j are present.
bool leftBag[maxSize][maxSize];
// Function to calculate the maximum
// value
int maxScore(vector money)
{
// we will fill the dp table
// in a bottom-up manner. fill
// all states that represent
// lesser number of bags before
// filling states that represent
// higher number of bags.
// we start from states dp(i, i)
// as these are the base case of
// our DP solution.
int n = money.size();
int totalTurns = n;
// turn = 1 if it is player 1's
// turn else 0. Who gets to pick
// the last bag(bottom-up so we
// start from last turn)
bool turn
= (totalTurns % 2 == 0) ? 0 : 1;
// if bag is picked by P1 add it
// to the ans else 0 contribution
// to score.
for (int i = 0; i < n; i++) {
dp[i][i] = turn ? money[i] : 0;
leftBag[i][i] = 1;
}
// 2nd last bag is picked by
// the other player.
turn = !turn;
// sz represents the size
// or number of bags in
// the state dp(i, i+sz)
int sz = 1;
while (sz < n) {
for (int i = 0; i + sz < n; i++) {
int scoreOne = dp[i + 1][i + sz];
int scoreTwo = dp[i][i + sz - 1];
// First player
if (turn) {
dp[i][i + sz]
= max(money[i] + scoreOne,
money[i + sz] + scoreTwo);
// if leftBag has more profit
if (money[i] + scoreOne
> money[i + sz] + scoreTwo)
leftBag[i][i + sz] = 1;
else
leftBag[i][i + sz] = 0;
}
// second player
else {
dp[i][i + sz]
= min(scoreOne,
scoreTwo);
if (scoreOne < scoreTwo)
leftBag[i][i + sz] = 1;
else
leftBag[i][i + sz] = 0;
}
}
// Give turn to the
// other player.
turn = !turn;
// Now fill states
// with more bags.
sz++;
}
return dp[0][n - 1];
}
// Using the leftBag matrix,
// generate the actual game
// moves that lead to the score.
string getMoves(int n)
{
string moves;
int left = 0, right = n - 1;
while (left <= right) {
// if the bag is picked from left
if (leftBag[left][right]) {
moves.push_back('L');
left++;
}
else {
moves.push_back('R');
right--;
}
}
return moves;
}
// Driver Code
int main()
{
int ar[] = { 10, 80, 90, 30 };
int arraySize = sizeof(ar) / sizeof(int);
vector bags(ar, ar + arraySize);
int ans = maxScore(bags);
cout << ans << " " << getMoves(bags.size());
return 0;
}
110 RRRL
这种方法的时间和空间复杂性是 。