Leetcode877.石子游戏(java实现)(普通人的思路)

题目可能确实没有出好,毕竟在一定程度上,我们能够很快地找到其中的“窍门”---->题目中的限制条件是1. 石头的堆数是偶数 2. 石头的总数是奇数,而先手的Alex可以决定Lee是选取奇数编号的石堆或偶数编号的石堆(即先手玩家可以单方面决定他们两个选取石堆的编号的奇偶性),因此Alex在这种条件下是必胜的!return true就可以ac这道题。

但是我们也应该明白出题人的用意,进行一般化的分析,用动态规划或其他方式去解决这道问题,并且按照出题人提供的信息,我们最好是能够判断Alex是否能赢,能赢多少分,当然,输出就不用输出赢多少分了(笑

由题目知道,我们每次只能拿两端的石头堆的石头,但我们也并不知道拿完剩下的石头堆的情况,因此我们考虑从最后一步开始去思考,怎么个从最后一步开始思考呢?

一:确定状态

最后一步、

首先我们设dp[i][j]为编号为i的石头堆到编号堆为j的石头堆之间,Alex最多可以赢Lee的分数,比如说,dp[i][i],即表示piles[i]~piles[i]之间Alex可以赢Lee的分数,而这种情况下,先手必胜,Alex只需要拿这个piles[i]就可以取胜了,而这就是最后一步。

子问题、

我们求出了一堆石头堆的胜负情况,那么我们也可以参考这种情况,求出2个相邻石头堆的胜负情况,也可以求出相邻3个石头堆的胜负情况,以此类推,我们可以根据n-1个相邻石头堆的胜负情况,求出n个相邻石头堆的胜负情况。

二:转移方程

如何求出2个相邻石头堆的胜负情况呢?仍然是先手必赢,Alex会选择最优策略,即选取最大的,把小的留给Lee。那么我们如何用转移方程,从单一的石头堆推出2个相邻石头堆的胜负情况呢?难道直接用max{piles[i], piles[j]}地对两侧的石头堆进行比较吗?回答当然是不能,这样的贪心策略在遇到{7,100,2,6}时就会出现错误。如果Alex贪心策略拿了第一堆石头’7’,那么Lee就可以拿到’100’而获胜了。

现在,我们已知的是dp[i][i],假设我们现在有两堆石头,那么我们已知piles[0]、piles[1]、dp[0][0]、dp[1][1],如何得到dp[0][1]?我们现在可以知道,dp[0][0]是Alex在单一石头堆情况下选取的最优策略,那么在两个石头堆的情况下,Alex的最优选取策略就变成了Lee的最优选取策略!因为在题目的描述中他们都是聪明的人并且会选择最优策略!也因此我们可以通过piles[0]-dp[1][1]表示Alex先手取走第一堆石头,而Lee后手选取第二堆石头,piles[1]-dp[0][0]亦是同理。通过比较piles[1]-dp[0][0]和piles[0]-dp[1][1],我们就能够得到在2个相邻石头堆的胜负情况。

在3个相邻石头堆的情况下亦是如此。假设石头堆为{3,4,5},在2个相邻石头堆的情况下,即{3,4}和{4,5}这两种情况,Alex在{3,4}中必然会选择4,在{4,5}中必然会选择5。那么在完整的3个相邻石头堆的情况下,Alex在2个相邻石头堆情况下的选择,就变成了Lee的选择。因此,dp[0][3] = max{pile[0] - dp[1][2] , pile[2] - dp[0][1] },pile[0]-dp[1][2]代表着,Alex拿取了[0]个石头堆后,减去Lee在[1]~[2]相邻石头堆情况下赢得的分数,即Alex在[0]-[2]情况下拿取0所能够赢取的分数;同理,pile[2] - dp[0][1] 表示Alex在[0]-[2]情况下拿取2所能够赢取的分数。
最终得到转移方程:
dp[i][j] = max { pile[i] - dp[i+1][j] , pile[j] - dp[i][j-1] }

三:初始条件

dp[i][i] = pile[i]

class Solution {
    public boolean stoneGame(int[] piles) {
        int n=piles.length;
        int [][]dp=new int[n][n];
        //初始化dp[i][i]
        for(int i=0;i<n;i++)
            dp[i][i]=piles[i];
        
        //注意这里的两个循环
        //第一个循环dis代表间隔距离,比如说dis=1时,配合接下来i的循环,
        //会不断得到相邻2个石头堆的最优选择策略,比如说{1,2,3,4时},会
        //得到{1,2}、{2、3}、{3、4}的最优选择策略;当dis=2时,会得到相
        //邻3个石头堆的最优选择策略,得到{1,2,3}、{2、3、4}。
        for(int dis=1;dis<n;dis++)
            for(int i=0;i<n-dis;i++)//i仍然表示起始位置
                dp[i][i+dis]=Math.max(piles[i]-dp[i+1][i+dis],piles[i+dis]-dp[i][i+dis-1]);
        return dp[0][n-1]>0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值