Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game)


亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

示例:

输入:[5,3,4,5]
输出:true
解释:
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。

提示:

  1. 2 <= piles.length <= 500
  2. piles.length 是偶数。
  3. 1 <= piles[i] <= 500
  4. sum(piles) 是奇数。

数学题,但我们用DP来求解这一题。

我们首先定义一个类,名为P:

private static class P {
int fir, sec; P(int fir, int sec) {
this.fir = fir;
this.sec = sec;
}
}

P中有两个属性,fir代表先手获得的最高分数,sec代表后手获得的最高分数

那么我们就可以写出DP的含义

我们把dp定义为2维,如dp[i][j]代表从这堆石子的第i堆选到第j堆中获得的最高分数。

那么dp[i][j].fir 表示从第i堆到第j堆中先手获得的最高分。

dp[i][j].sec 表示从第i堆到第j堆中后手获得的最高分。

例如:piles = [5,3,4,5]

dp[0][1].fir = 5, 面对[5,3],先手可以得5分

dp[0][1].sec = 3,面对[5,3],后手可以得3分

我们首先明确一点,当 i == j 时,即只有一堆石子的时候,上面的例子,例如 i == j == 0 那么 dp[i][j].fir = 5 sec=0

所以我们把i==j时的所有情况,遍历一遍。

for (int i = 0; i < n; i++) {
dp[i][i].fir = piles[i];
dp[i][i].sec = 0;
}

下面看一张图,是最终dp数组的最后状态:

我们可以看到,dp[0][1] = (9,3)    即dp[0][1].fir = 9    sec=3

这个数值是由(0,0)和(1,1)一起生成的。

我们可以按层遍历,即:

第1层:对角线

第2层:(9,3) (9,1) (2,1)

第3层:(4,9) (10,2)

第4层:(11,4)

选择了按层遍历后,我们需要得到状态转移方程:

面对一堆石头,我们有如下情况可以选择:

1、我是先手

  • 我选左边,面对剩下的piles[i+1,j]
  • 我选右边,面对剩下的piles[i,j-1]

随后对方变成先手,我变成后手

dp[i][j].fir = max(piles[i]+dp[i+1][j].sec,piles[j]+dp[i][j-1].sec);

2、我是后手

  • 先手选择了左边的那堆,我只能选择剩下的,dp[i][j].sec = dp[i+1][j].fir;
  • 先手选择了右边的那堆,我只能选择剩下的,dp[i][j].sec = dp[i][j-1].fir;

随后对方变成后手,我变成了先手


class Solution {
private static class P {
int fir, sec; P(int fir, int sec) {
this.fir = fir;
this.sec = sec;
}
} public boolean stoneGame(int[] piles) { int n = piles.length;
P[][] dp = new P[n + 1][n + 1];
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
dp[i][j] = new P(0, 0);
}
}
for (int i = 0; i < n; i++) {
dp[i][i].fir = piles[i];
dp[i][i].sec = 0;
} for (int c = 2; c <= n; c++) {
for (int i = 0; i <= n - c; i++) {
int j = c + i - 1;
int left = piles[i] + dp[i + 1][j].sec;
int right = piles[j] + dp[i][j - 1].sec;
if (left > right) {
dp[i][j].fir = left;
dp[i][j].sec = dp[i + 1][j].fir;
} else {
dp[i][j].fir = right;
dp[i][j].sec = dp[i][j - 1].fir;
} }
}
return dp[0][n - 1].fir > dp[0][n - 1].sec;
}
}

#本题思路来自题解区

Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game)的更多相关文章

  1. leetcode 877. 石子游戏

    题目描述: 亚历克斯和李用几堆石子在做游戏.偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] . 游戏以谁手中的石子最多来决出胜负.石子的总数是奇数,所以没有平局. 亚历克斯和李轮流进行,亚 ...

  2. 动态规划dp专题练习

    貌似开坑还挺好玩的...开一个来玩玩=v=... 正好自己dp不是很熟悉,就开个坑来练练吧...先练个50题?小目标... 好像有点多啊QAQ 既然是开坑,之前写的都不要了! 50/50 1.洛谷P3 ...

  3. [Swift]LeetCode877. 石子游戏 | Stone Game

    Alex and Lee play a game with piles of stones.  There are an even number of piles arranged in a row, ...

  4. Leetcode之动态规划(DP)专题-486. 预测赢家(Predict the Winner)

    Leetcode之动态规划(DP)专题-486. 预测赢家(Predict the Winner) 给定一个表示分数的非负整数数组. 玩家1从数组任意一端拿取一个分数,随后玩家2继续从剩余数组任意一端 ...

  5. Leetcode之动态规划(DP)专题-1025. 除数博弈(Divisor Game)

    Leetcode之动态规划(DP)专题-1025. 除数博弈(Divisor Game) 爱丽丝和鲍勃一起玩游戏,他们轮流行动.爱丽丝先手开局. 最初,黑板上有一个数字 N .在每个玩家的回合,玩家需 ...

  6. Leetcode之动态规划(DP)专题-详解983. 最低票价(Minimum Cost For Tickets)

    Leetcode之动态规划(DP)专题-983. 最低票价(Minimum Cost For Tickets) 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行.在接下来的一年里,你要旅行的 ...

  7. Leetcode之动态规划(DP)专题-647. 回文子串(Palindromic Substrings)

    Leetcode之动态规划(DP)专题-647. 回文子串(Palindromic Substrings) 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同开始位置或结束位置的子 ...

  8. Leetcode之动态规划(DP)专题-474. 一和零(Ones and Zeroes)

    Leetcode之动态规划(DP)专题-474. 一和零(Ones and Zeroes) 在计算机界中,我们总是追求用有限的资源获取最大的收益. 现在,假设你分别支配着 m 个 0 和 n 个 1. ...

  9. Leetcode之动态规划(DP)专题-264. 丑数 II(Ugly Number II)

    Leetcode之动态规划(DP)专题-264. 丑数 II(Ugly Number II) 编写一个程序,找出第 n 个丑数. 丑数就是只包含质因数 2, 3, 5 的正整数. 示例: 输入: n ...

随机推荐

  1. MySQL 关于存储过程的操作

    -- 存储过程 --> 一组为了完成特定功能的SQL语句集 -- IN 传入参数 delimiter // create procedure mysql ( IN id int ) begin ...

  2. spark 三种数据集的关系(二)

    一个Dataset是一个分布式的数据集,而且它是一个新的接口,这个新的接口是在Spark1.6版本里面才被添加进来的,所以要注意DataFrame是先出来的,然后在1.6版本才出现的Dataset,提 ...

  3. mongodb 批量添加、修改和删除

    1.使用MongoTemplate a.批量插入 Insert a Collection of objects into a collection in a single batch write to ...

  4. SpringBoot AOP介绍

    说起spring,我们知道其最核心的两个功能就是AOP(面向切面)和IOC(控制反转),这边文章来总结一下SpringBoot如何整合使用AOP. 一.示例应用场景:对所有的web请求做切面来记录日志 ...

  5. react-缓存

    目录结构: 用到缓存的地方: 缓存的方法 清楚缓存

  6. Servlet中关于路径的小结

    URL(统一 资源定位符).URI(统一资源标识符)都是由资源路径和资源名称组成.   访问路径按照路径是否可以独立完成资源准确定位的判别标准,可以将访问路径分为绝对路径与相对路径. 关系:绝对路径 ...

  7. websocket 连接测试端口服务是否正常代码

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. docker Tomcat镜像

    docker中的管理命令 首先需要去下载镜像. [root@CFCA ~]# docker pull tomcat    下载镜像[root@CFCA ~]# docker images       ...

  9. 用 Docker 搭建 ORACLE 数据库开发环境

    用 Docker 搭建 ORACLE 数据库开发环境 需要安装 ORACLE 数据库做开发,直接安装的话因为各类平台的限制,非常复杂,会遇到很多问题. 还好,现在有 Docker 化的部署方式,省去很 ...

  10. [心得]暑假Day 8

    em. 一波爆炸后回到了一个原始位置rank33 最近两场考试没啥状态 总感觉都读不懂题目了 T1 因为有的边要经过两次,不妨把边复制成双倍,那么再去掉2条,如果能一遍把剩下的边过完,也就是成为一笔画 ...