问题描述
小S最近在分析一个数组 ℎ1,ℎ2,...,ℎ𝑁,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意 𝑘 个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于 𝑘 个相邻的元素,我们定义其矩形的最大面积为:
𝑅(𝑘)=𝑘×𝑚𝑖𝑛(ℎ[𝑖],ℎ[𝑖+1],...,ℎ[𝑖+𝑘−1])R(k)=k×min(h[i],h[i+1],...,h[i+k−1])
即,𝑅(𝑘) 的值为这 k 个相邻元素中的最小值乘以 𝑘。现在,小S希望你能帮他找出对于任意 𝑘,𝑅(𝑘) 的最大值。
测试样例
样例1:
输入:
n = 5, array = [1, 2, 3, 4, 5]
输出:9
样例2:
输入:
n = 6, array = [5, 4, 3, 2, 1, 6]
输出:9
样例3:
输入:
n = 4, array = [4, 4, 4, 4]
输出:16
问题分析
分析题目,要求计算 k 个相邻元素中最小值乘以 k 的最小值,当 k 值增加的时候,k 增大,最小值元素可能会降低。(1) 考虑当 k 增加到 k + 1时, R(k)最大值的变化; (2)考虑数组长度从 N 到 N + 1时,R(k) 最大值的变化;因此这道题很明显时利用动态规划的方式来求解。
定义状态
定义状态dp(i,j),表示当数组长度不超过 j,k值不超过 i 的情况下,R的最大值。
初始条件
- min(i,j) 表示从 j-i+1 到 j的数组的最小值。
- min(1,j) = A(j)。
- dp(1,1) = A(1),当 k 值为1时, N =1, 截取长度为1,R(k)等于数组元素本身。
- dp(1,j) = max{ dp(1, j-1), min(1, j)}。表示是否考虑第 j 个数。
状态转移方程
dp(i, j) = max{ dp(i, j-1), min(i, j) * i, dp(i-1, j) }
状态转移分为两步:(1)当 j 增大到 j+1 时,R(k)最大值变化。(2)当 k 增大到 k+1 时, R(k)最大值变化。
解题步骤
根据定义状态、设置初始条件和状态转移方程的顺序,得到解题步骤为:
- 通过遍历得到min(i,j)矩阵,
mins[i][j] = Math.min(mins[i-1][j], array[j-i]);,当 k 和 N 长度变化,都会影响子数组的最小值。 - 设置初始条件,即dp(1,j)的值。
- 实现状态转移方程,当 i 从1遍历到n,j从1遍历到n, 每次状态转移得到的dp(i,j)值。
- dp(n,n)表示当数组长度不超过 n,k值不超过 n 的情况下,R的最大值。即最终的问题输出。
完整代码实现
import java.util.Arrays;
public class Main {
public static int solution(int n, int[] array) {
// 定义状态
int[][] dp = new int[n][n]; // dp[i][j] 表示前面索引不超过i个数据中,选择不超过j个数的最大R值。
// 初始化
for(int i=0; i<n; i++){
Arrays.fill(dp[i], 0);
}
int[][] mins= new int[n][n];
for(int i=0; i<n; i++){
mins[0][i] = array[i];
}
for(int i=1; i<n; i++){
for(int j=i; j<n; j++){
mins[i][j] = Math.min(mins[i-1][j], array[j-i]);
}
}
// 第一个数,只选一个数,最大R值就是它本身。
dp[0][0] = array[0];
// 索引不超过j,只选一个数,最大R值就是它本身,或者前面j-1个数中的最大R
for(int j=1; j<n; j++){
dp[0][j] = Math.max(dp[0][j-1], array[j]);
}
// // 索引不超过i,选择不超过i个数的最大R值。
// for(int i=1; i<n; i++){
// dp[i][i] = Math.max(dp[i-1][i], mins[i]*(i+1));
// }
for(int i=1; i<n; i++){
for(int j=i; j<n; j++){
dp[i][j] = Math.max(dp[i-1][j], mins[i][j]*(i+1));
dp[i][j] = Math.max(dp[i][j], dp[i][j-1]);
}
}
return dp[n-1][n-1];
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}) == 9);
}
}
问题总结
本题考察的是动态规划的求解问题,强调如何通过分析问题找到动态规划求解问题的思路。问题如果存在状态转移方程,极有可能通过动态规划的思路来求解。步骤包括定义状态、设置初始条件和定义状态转移方程。