Lincode 670. 预测能否胜利

题目描述:
给一由非负整数构成的分数数组. 玩家1 从数组的任意一端选择一个数字, 然后是玩家2, 然后又是玩家1 等等. 每一次一个玩家都只取一个数, 每个数只能取一次. 等到数组内分数都被取完后, 分数大的那个玩家获胜.
给一分数数组, 预测玩家1 是否是赢家.你可以假设每个玩家都想要使他的分数尽可能的大.
样例
给出 nums = [1, 5, 2] 返回 false
(最初, 玩家1 可以选择 1 或 2. 如果他选择 2 (或 1), 那么玩家2 可以选择 1(或 2 ) 或 5. 如果玩家2 选择了 5, 那么玩家1 只能选择 1(或2), 所以玩家1 最终的分数为 1 + 2 = 3, 而玩家2 为5. 所以玩家1 永远都不会是胜者, 你需要返回 false)

给出 nums = [1, 5, 233, 7] 返回 true
(玩家1 先选择 1. 玩家2 可以选择 5 或 7. 不管玩家2 选择了哪一个, 玩家1 可以选择 233. 最终, 玩家1(234) 会比玩家2(12) 分数更高, 所以你需要返回 true 代表玩家1 可以赢)

这是一道典型的区间型动态规划题目,通过这道题目可以总结一下区间型动态规划的解法。
根据动态规划的步骤,
1、首先确定状态转移方程:要求求玩家1的最大分数,我们假设一个二维的数组dp,dp[i][j]代表是在从第i到j位先选的那位玩家会比后选的那位玩家多出来的分数,然后我们考虑如何更新dp数组,再回到这个问题考虑一下,每位玩家要么从左边选择,要么从右边选择。
假设他们现在在第i到j位里面选数字,现在刚好轮到A,如果A选择了位置i,A就获得了nums[i]的分数,然后B只能从位置i+1到位置j中选择了,根据我们dp的定义,dp[i+1][j]代表的是先选的那位玩家可以比后选的那位玩家多出来的分数,所以在位置i+1到j中获得分数时,玩家B将比玩家A多出dp[i+1][j]的分数,所以玩家A一旦选择了位置i的值,那么他的最终获得的分数情况将会比玩家B多出nums[i]-dp[i+1][j]的分数,同理,玩家A选择了j位置,那么玩家A将会比玩家B多出nums[j]-dp[i][j-1]的分数。
由此可得到递推关系:dp[i][j]=Math.max(nums[i]-dp[i+1[j],nums[j]-dp[i][j-1]);
2、确定初始状态:区间型动态化的初始状态为对角线,填充对角线dp[i][i] = nums[i];
对于例子 nums = [1, 5, 233, 7] ,建立的动态规划表如图所示:
这里写图片描述
3、最后得到答案:return dp[0][length - 1] >= 0;若第一行的最后一个数大于0,则表示玩家1是获胜者。

附C++ AC代码:

int dp(vector<int> &nums)
{
    int length = nums.size();
    vector<vector<int>> dp(length, vector<int>(length));

    for(int i = 0; i < length; ++i)
    {
        dp[i][i] = nums[i];
    }

    for(int len = 1; len < length; ++len)
    {
        for(int i = 0; i < length - len; ++i)
        {
            int j = i + len;
            dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
        }
    }
    return dp[0][length - 1] >= 0;
}

参考链接:https://leetcode.com/problems/predict-the-winner/discuss/96828/java-9-lines-dp-solution-easy-to-understand-with-improvement-to-on-space-complexity

猜你喜欢

转载自blog.csdn.net/sinat_19596835/article/details/80621492