考虑一排 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++
// 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;
}
Python3
# Python3 implementation
# Calculates the maximum score
# possible for P1 If only the
# bags from beg to ed were available
def maxScore( money, beg, ed):
# Length of the game
totalTurns = len(money)
# Which turn is being played
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
scoreOne = maxScore(money, beg + 1, ed)
# Player picks money from
# the right end
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[0] >
money[ed] + scoreTwo[0]):
return [money[beg] + scoreOne[0],
"L" + scoreOne[1]]
else:
return [money[ed] + scoreTwo[0],
"R" + scoreTwo[1]]
# 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[0] < scoreTwo[0]):
return[scoreOne[0], "L" + scoreOne[1]]
else:
return[scoreTwo[0], "R" + scoreTwo[1]]
# Driver Code
# Input array
ar = [ 10, 80, 90, 30 ]
arraySize = len(ar)
bags = ar
# Function Calling
ans = maxScore(bags, 0, arraySize - 1)
print(ans[0], ans[1])
# This code is contributed by shivani
Javascript
C++
// 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
上述方法的时间复杂度是指数级的。
最佳方法:
我们可以通过使用动态规划来解决这个问题时间和空间复杂度。
- 如果我们为从某个开头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++
// 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
这种方法的时间和空间复杂度是 .
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。