LeetCode 486 Predict the Winner 题解

题目简述:给定非负数组nums。有两个玩家,从玩家1开始交替取出一个数并加到他的分数上,取的时候只能取数组剩余的头尾两个数之一。当所有数都被取完时分数高的玩家获胜。求在给定nums的情况下,预测玩家1是否获胜。假设两个玩家每次都会做出最优选择。
输入:非负数组nums。
输出:一个bool值,表示玩家1是否获胜。
示例:对于数组[1,5,2],玩家1必输,因为无论取1还是2,玩家2都会取5而获胜。


题解:
这是一个最大最小策略的博弈问题。考虑使用动态规则求在某个状态下所能获得的分数最大值。记 sum(x,y)=yi=xnums[i] 表示区间[x,y]的数之和, dp_max(x,y) dp_min(x,y) 表示在区间[x,y]通过最优策略所能获得的最大分数和最小分数。
状态转移方程是:

dp_max(i,j)=max(xi+dp_min(i+1,j),xj+dp_min(i,j1))

简单解释一下,对于区间[i,j],都是两种取法,那么就取这两个方法中更高分数的那个。一旦取了一个数,由于对手也会做最优选择,因此你在剩余的区间得到的分数肯定是最小的。
然后根据 sum(x,y)=dp_max(x,y)+dp_min(x,y) ,可以对状态转移方程进行变形,使其不出现 dp_min(x,y)
dp_max(i,j)=max(xi+dp_min(i+1,j),xj+dp_min(i,j1))=max(xi+sum(i+1,j)dp_max(i+1,j),xj+sum(i,j1)dp_max(i,j1))=max(sum(i,j)dp_max(i+1,j),sum(i,j)dp_max(i,j1))=sum(i,j)min(dp_max(i+1,j),dp_max(i,j1))

初始状态是 dp_max(i,i)=nums[i]
这个状态转移方程显然可以用递推实现,先从区间长度为1的 dp_max(i,i) 出发,求出区间长度为2的 dp_max(i,i+1) ,依次增加区间长度,直至求目标状态 dp_max(0,n1) ,表示玩家1所能获得的分数最大值,如果它大于或等于总分的一半,则玩家1必胜。

最后进行空间上的优化,注意到求出 dp_max(i,j) 只需要它上一级的 dp_max(i,j1) 以及相邻的 dp_max(i+1,j) ,因此可以改二维为一维,只需要求dp的时候从左到右计算即可。新的状态转移方程是:

dp_max(i)=sum(i,i+j)min(dp(i),dp(i+1)),j

算法实现如下,时间复杂度是O( N2 ),空间复杂度是O(N)。

class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int dp[21], sum[21];

        sum[0] = 0;
        for(int i = 0;i < nums.size();i++) {
            sum[i+1] = sum[i] + nums[i];
            dp[i] = nums[i];
        }

        for(int i = 1;i < nums.size();i++) {
            for(int j = 0;j < nums.size() - i;j++) {
                dp[j] = sum[j+i+1] - sum[j] - min(dp[j], dp[j+1]);
            }
        }

        if(2*dp[0] >= sum[nums.size()]) return true;
        else return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值