硬币收集 2 人游戏
Coin collection 2 player game
考虑一个包含正整数值的 n 个硬币和两个玩家 player1 和 player2 的数组。每个玩家轮流拿硬币,直到剩下硬币为止。那个
最大值获胜。玩家可以拿走的硬币数量由变量 S 初始 =1 决定,玩家可以从左边开始连续拿走 k 个硬币,其中 1<=k
<=2*S 而在玩家之后假设有 k 个硬币,S 的值变为 max(S,k)。此外,假设双方都采取最优策略。 我们必须找到玩家 1 在游戏中可以拿走的最大
硬币值
例如:如果输入是 [3,6,8,5,4],输出应该是 12,因为如果玩家 1 拿了一个硬币,玩家 2 拿了 2 个硬币,然后玩家 1 再拿了 2 个硬币。所以玩家 1
将有 3 + 5 + 4 = 12。
我的想法:我觉得可能有一些方法可以使用动态规划来完成,但我找不到子问题或最优子结构。条件看起来很复杂。关于如何处理这个问题有什么想法吗?
子问题被识别为:
- 已经拿走的硬币数量。否则放置:硬币数组中的索引,可以从中获取下一个硬币。
- S的价值
由于可以拿走的硬币数量和S的值永远不会超过硬币数量,我们可以使用大小为的矩阵来记忆结果。
对于memoization而言,轮次并不重要:无论轮到谁,他们在相同的状态下都有相同的机会。因此,如果我们遇到玩家 1 的状态并对其进行评估(最大化硬币价值),然后再遇到相同的状态,但玩家 2 正在玩,那么之前的结果可以应用于玩家 2.
算法可以使用递归。基本情况发生在当前玩家可以决定拿走所有剩余的硬币时。当然,这永远是最好的一步。
对于递归的情况,当前玩家的所有可能走法都可以进行。对于每一个对手的最佳分数可以通过递归调用得出。当前玩家的最佳着法是对手的最佳得分为 minimized.
的着法
这是 JavaScript 中的一个实现。 运行 此代码段将解决您在问题中提出的问题,以及 [1,1,1,1,1,1,1]:
function solve(coins) {
let n = coins.length;
// Preprocessing: for all possibly suffix arrays, calculate the sum of the coins
let sums = coins.slice(); // copy
for (let i = n - 2; i >= 0; i--) {
sums[i] += sums[i + 1]; // backwards running sum
}
// 2D Array for memoization (dynamic programming)
let dp = []; // n x n matrix, initially filled with -1
for (let i = 0; i < n; i++) dp.push(Array(n).fill(-1));
return recur(0, 1);
function recur(start, s) {
if (n - start <= 2*s) return sums[start]; // base case: take all remaining coins
if (dp[start][s] == -1) { // sub problem was not encountered before
let minOpponentScore = Infinity;
// For each possible move, get best counter move from opponent
for (let k = 1; k <= 2*s; k++) {
// We'll take the move where the best counter move was the worst
minOpponentScore = Math.min(minOpponentScore, recur(start + k, Math.max(k, s)));
}
dp[start][s] = sums[start] - minOpponentScore;
}
return dp[start][s];
}
}
console.log(solve([3,6,8,5,4]));
console.log(solve([1,1,1,1,1,1,1]));
考虑一个包含正整数值的 n 个硬币和两个玩家 player1 和 player2 的数组。每个玩家轮流拿硬币,直到剩下硬币为止。那个
最大值获胜。玩家可以拿走的硬币数量由变量 S 初始 =1 决定,玩家可以从左边开始连续拿走 k 个硬币,其中 1<=k
<=2*S 而在玩家之后假设有 k 个硬币,S 的值变为 max(S,k)。此外,假设双方都采取最优策略。 我们必须找到玩家 1 在游戏中可以拿走的最大
硬币值
例如:如果输入是 [3,6,8,5,4],输出应该是 12,因为如果玩家 1 拿了一个硬币,玩家 2 拿了 2 个硬币,然后玩家 1 再拿了 2 个硬币。所以玩家 1
将有 3 + 5 + 4 = 12。
我的想法:我觉得可能有一些方法可以使用动态规划来完成,但我找不到子问题或最优子结构。条件看起来很复杂。关于如何处理这个问题有什么想法吗?
子问题被识别为:
- 已经拿走的硬币数量。否则放置:硬币数组中的索引,可以从中获取下一个硬币。
- S的价值
由于可以拿走的硬币数量和S的值永远不会超过硬币数量,我们可以使用大小为的矩阵来记忆结果。
对于memoization而言,轮次并不重要:无论轮到谁,他们在相同的状态下都有相同的机会。因此,如果我们遇到玩家 1 的状态并对其进行评估(最大化硬币价值),然后再遇到相同的状态,但玩家 2 正在玩,那么之前的结果可以应用于玩家 2.
算法可以使用递归。基本情况发生在当前玩家可以决定拿走所有剩余的硬币时。当然,这永远是最好的一步。
对于递归的情况,当前玩家的所有可能走法都可以进行。对于每一个对手的最佳分数可以通过递归调用得出。当前玩家的最佳着法是对手的最佳得分为 minimized.
的着法这是 JavaScript 中的一个实现。 运行 此代码段将解决您在问题中提出的问题,以及 [1,1,1,1,1,1,1]:
function solve(coins) {
let n = coins.length;
// Preprocessing: for all possibly suffix arrays, calculate the sum of the coins
let sums = coins.slice(); // copy
for (let i = n - 2; i >= 0; i--) {
sums[i] += sums[i + 1]; // backwards running sum
}
// 2D Array for memoization (dynamic programming)
let dp = []; // n x n matrix, initially filled with -1
for (let i = 0; i < n; i++) dp.push(Array(n).fill(-1));
return recur(0, 1);
function recur(start, s) {
if (n - start <= 2*s) return sums[start]; // base case: take all remaining coins
if (dp[start][s] == -1) { // sub problem was not encountered before
let minOpponentScore = Infinity;
// For each possible move, get best counter move from opponent
for (let k = 1; k <= 2*s; k++) {
// We'll take the move where the best counter move was the worst
minOpponentScore = Math.min(minOpponentScore, recur(start + k, Math.max(k, s)));
}
dp[start][s] = sums[start] - minOpponentScore;
}
return dp[start][s];
}
}
console.log(solve([3,6,8,5,4]));
console.log(solve([1,1,1,1,1,1,1]));