1. 题目
读题
考查点
2. 解法
思路
动态规划是一种将复杂问题分解为子问题的方法,通过记录子问题的解,避免重复计算,从而提高效率。动态规划的关键是找到状态和状态转移方程。
在这道题中,我们可以定义状态为dp[i],表示第i个位置能接住的雨水量。状态转移方程为:
- dp[i] = min(maxLeft[i], maxRight[i]) - height[i],其中maxLeft[i]表示第i个位置左边的最大高度,maxRight[i]表示第i个位置右边的最大高度。这个方程的含义是,第i个位置能接住的雨水量等于它左右两边最大高度的较小值减去它自身的高度。
- 如果min(maxLeft[i], maxRight[i]) - height[i]小于0,那么dp[i]等于0,表示第i个位置不能接住雨水。
代码逻辑
动态规划这个实现的代码逻辑。代码的逻辑可以分为以下几个步骤:
- 创建三个数组,分别用来存储每个位置左边的最大高度,右边的最大高度和能接住的雨水量。
- 从左往右遍历原数组,更新左边最大高度数组,利用一个变量记录当前遇到的最大高度。
- 从右往左遍历原数组,更新右边最大高度数组,利用一个变量记录当前遇到的最大高度。
- 遍历原数组,根据状态转移方程更新能接住雨水量数组和总雨水量,利用一个变量记录总雨水量。
- 返回总雨水量作为答案。
具体来说,代码的逻辑如下:
- 定义一个变量n,用来记录原数组的长度。
- 创建一个长度为n的数组maxLeft,用来存储每个位置左边的最大高度。
- 创建一个长度为n的数组maxRight,用来存储每个位置右边的最大高度。
- 创建一个长度为n的数组dp,用来存储每个位置能接住的雨水量。
- 定义一个变量waterTrapped,用来记录总雨水量,初始为0。
- 用一个循环从左往右遍历原数组,更新maxLeft数组。具体地,如果当前位置i大于0,那么maxLeft[i]等于maxLeft[i-1]和height[i-1]中的较大值。
- 用一个循环从右往左遍历原数组,更新maxRight数组。具体地,如果当前位置i小于n-1,那么maxRight[i]等于maxRight[i+1]和height[i+1]中的较大值。
- 用一个循环遍历原数组,根据状态转移方程更新dp数组和waterTrapped值。具体地,dp[i]等于maxLeft[i]和maxRight[i]中的较小值减去height[i]。如果dp[i]小于0,那么将dp[i]赋值为0。然后将dp[i]累加到waterTrapped中。
- 返回waterTrapped作为答案。
具体实现
class Solution { public int trap(int[] height) { // 动态规划法 int n = height.length; // 数组长度 int[] maxLeft = new int[n]; // 记录每个位置左边的最大高度 int[] maxRight = new int[n]; // 记录每个位置右边的最大高度 int[] dp = new int[n]; // 记录每个位置能接住的雨水量 int waterTrapped = 0; // 接住的雨水量 // 从左往右遍历数组,更新maxLeft数组 for (int i = 1; i < n; i++) { maxLeft[i] = Math.max(maxLeft[i-1], height[i-1]); } // 从右往左遍历数组,更新maxRight数组 for (int i = n - 2; i >= 0; i--) { maxRight[i] = Math.max(maxRight[i+1], height[i+1]); } // 遍历数组,根据状态转移方程更新dp数组和waterTrapped值 for (int i = 0; i < n; i++) { dp[i] = Math.max(0, Math.min(maxLeft[i], maxRight[i]) - height[i]); waterTrapped += dp[i]; } return waterTrapped; // 返回接住的雨水量 } }