【问题标题】:Coin collection 2 player game硬币收集2人游戏
【发布时间】:2021-12-28 05:44:23
【问题描述】:

考虑一个包含 n 个正整数值的硬币和两个玩家 player1 和 player2 的数组。每个玩家轮流拿硬币,直到剩下硬币。那个有
最大值获胜。玩家可以拿走的硬币数量由变量 S 初始 =1 控制,玩家可以从左边连续拿走 k 个硬币,其中 1我们必须找到最大数量
玩家1可以在游戏中获得的硬币价值
例如:如果输入是 [3,6,8,5,4],输出应该是 12,因为如果玩家 1 拿了一个硬币,玩家 2 拿了 2 个硬币,然后玩家 1 重新拿了 2 个硬币。所以player1
将有 3 + 5 + 4 = 12。
我的想法:我觉得可以使用动态编程来实现它,但我找不到子问题或最优子结构。条件看起来非常复杂。关于如何解决这个问题的任何想法?

【问题讨论】:

  • 如果找不到子问题,是什么让您认为动态规划是一种可能/好的方法?
  • 子问题由剩余的硬币和 S 的值定义。例如,如果 player1 拿了一枚硬币,则子问题是 [6,8,5,4]S=1。但是如果 player1 拿了两个硬币,子问题是 [8,5,4]S=2

标签: algorithm dynamic-programming


【解决方案1】:

子问题通过以下方式识别:

  • 已拿走的硬币数量。否则输入:coins 数组中的索引,可以从中取出下一个硬币。
  • S的值。

由于可以取出的硬币数量和S的值永远不会超过硬币数量?,我们可以使用大小为?的矩阵来记忆结果。

轮到记忆并不重要:无论轮到谁,他们在相同的状态下都有相同的机会。因此,如果我们遇到玩家 1 的状态并评估它(最大化硬币价值),然后遇到相同的状态,但玩家 2 可以玩,那么之前的结果可以应用于玩家 2。

算法可以使用递归。基本情况发生在当前玩家可以决定拿走所有剩余的硬币时。当然,这永远是最好的选择。

对于递归情况,可以播放当前玩家的所有可能动作。对于每一个对手的最佳得分都可以通过递归调用得出。当前玩家的最佳走法是让对手的最佳得分最小化

这是 JavaScript 中的一个实现。运行这个 sn-p 将解决您在问题中提出的问题,以及 [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]));

【讨论】:

  • 那是天才的一击。非常感谢。只是想知道一些关于你的事情。你做什么工作?您从事软件开发吗?
  • 我在 91 年代开始担任专业软件开发人员,从 80 年代中期开始迷上它。演变为 IT 项目经理。
  • 酷。你最喜欢的软件书籍是什么?您认为必须阅读以深入了解事物的内容。我是一名 Java 开发人员,但很想深入研究。
  • 我真的不能推荐一本书。我上一次阅读有关软件开发的文章已经很多年了。
猜你喜欢
  • 2021-03-12
  • 2020-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多