day12 题目:64. 最小路径和300. 最长递增子序列bytedance-004. 机器人跳跃问题

学习计划链接:冲刺春招 - 精选笔面试 66 题大通关

今日知识点:数组、动态规划、二分,难度为中等、中等、字节の简单

昨日题目链接:冲刺春招 - 精选笔面试 66 题大通关 day11

# 64. 最小路径和

给定一个包含非负整数的  m x n  网格  grid  ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明: 每次只能向下或者向右移动一步。

示例 1:

输入: grid = [[1,3,1],[1,5,1],[4,2,1]]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入: grid = [[1,2,3],[4,5,6]]
输出: 12

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

# 思路

一眼动态规划的题目

  • dp [i][j] 表示到达 (i,j) 这个点的最小路径和
  • 转移公式为 dp [i][j] = min (dp [i-1][j], dp [i][j-1]) + grid [i][j]
  • 先初始化第一行第一列,由于 dp 过程中只涉及相邻的两个结点,故可以将 grid 原来的值直接覆盖。

# 代码

/**
 * @param {number[][]} grid
 * @return {number}
 */
 var minPathSum = function(grid) {
    let [m, n] = [grid.length, grid[0].length];
    for(let i = 1; i < m; ++i) 
        grid[i][0] = grid[i-1][0] + grid[i][0]; // 第一列
        
    for(let i = 1; i < n; ++i) 
        grid[0][i] = grid[0][i-1] + grid[0][i]; // 第一行
    for(let i = 1; i < m; ++i)
        for(let j = 1; j < n; ++j)
            grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1]) + grid[i][j];    // 左侧和上侧中走过来较小的呢个
    return grid[m-1][n-1];
};

# 300. 最长递增子序列

给你一个整数数组  nums  ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如, [3,6,2,7]  是数组  [0,3,1,6,2,2,7]  的子序列。

示例 1:

输入: nums = [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入: nums = [0,1,0,3,2,3]
输出: 4

示例 3:

输入: nums = [7,7,7,7,7,7,7]
输出: 1

提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

进阶:

  • 你能将算法的时间复杂度降低到  O(n log(n))  吗?

# 思路

动态规划, dp[i] 表示 0~i 中最长递增子序列长度,每次在 0i-1 中找可以与当前 i 构成递增序列的 j ,取其中最大的那个作为 dp[i]

# 代码

/**
 * @param {number[]} nums
 * @return {number}
 */
 var lengthOfLIS = function(nums) {
    let len = nums.length;
    let dp = new Array(len).fill(1);    //dp [i] 表示 0~i 中最长递增子序列长度
    let ans = 1;
    for(let i = 1; i < len; ++i) {
        for(let j = 0; j <= i-1; ++j) {
            if(nums[i] > nums[j])  // 大,可以与当前 i 构成递增序列
                dp[i] = Math.max(dp[i], dp[j]+1);
        }
        ans = Math.max(ans, dp[i]);
    }
    return ans;
};

# bytedance-004. 机器人跳跃问题

机器人正在玩一个古老的基于 DOS 的游戏。游戏中有 N+1 座建筑 —— 从 0 到 N 编号,从左到右排列。编号为 0 的建筑高度为 0 个单位,编号为 i 的建筑的高度为 H (i) 个单位。
起初, 机器人在编号为 0 的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第 k 个建筑,且它现在的能量值是 E, 下一步它将跳到第个 k+1 建筑。它将会得到或者失去正比于与 H (k+1) 与 E 之差的能量。如果 H (k+1) > E 那么机器人就失去 H (k+1) - E 的能量值,否则它将得到 E - H (k+1) 的能量值。
游戏目标是到达第个 N 建筑,在这个过程中,能量值不能为负数个单位。现在的问题是机器人以多少能量值开始游戏,才可以保证成功完成游戏?

格式:

输入:
- 第一行输入,表示一共有 N 组数据.
- 第二个是 N 个空格分隔的整数,H1, H2, H3, ..., Hn 代表建筑物的高度
输出:
- 输出一个单独的数表示完成游戏所需的最少单位的初始能量

示例 1:
输入

5
3 4 3 2 4

输出

4

示例 2:

输入

3
4 4 4

输出

4

示例 3:

3
1 6 4

提示:

  • 1 <= N <= 10^5
  • 1 <= H(i) <= 10^5

# 思路

没什么思路,去看了一眼评论恍然大悟。这题核心是解一下方程,虽然题意忽悠我们说了一大堆,实际上是这样的:
设当前编号为 i ,高度为 h[i] ,能量为 e[i] ,则想到下一个点有 e[i+1] = e[i]+(e[i]-h[i+1]) 或者 e[i+1] = e[i]-(h[i+1]-e[i]) ,可以发现这两者其实都是等价 e[i+1] = 2*e[i]-h[i+1] 的,故当最后一个也就是 e[n-1] 为 0 时初始所需能量最少,即 2*e[i]-h[i+1] = 0 时,解得 e[i] = h[i+1]/2 (向上取整)那么从后往前遍历一遍即可。

# 代码

#include <iostream>
using namespace std;
const int maxn = 100005;
int n, h[maxn], E;
int main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin >> n;
    for(int i = 0; i < n; ++i) 
        cin >> h[i];
    for(int i = n-1; i >= 0; --i)
        E = (E+h[i]+1)/2;
    cout << E << endl;
    return 0;
}