题目来源:https://leetcode-cn.com/problems/optimal-division/
大致题意:
给一个数组,相邻元素之间会进行除法操作,可以在元素之间加上括号改变除法运算的顺序,求出如何加括号可以使最后运算结果的值最大。并返回最后的除法组成的字符串
思路
可以通过动态规划来解题。
使用 dp[i][j] 表示索引 i 至 j 的元素能组成的除法的最大值和最小值,以及对应的字符串,那么
- dp[i][j] 的最大值等于dp[i][k]的最大值除以dp[k + 1][j] 的最小值,其中 i <= k < j
- dp[i][j] 的最小值等于dp[i][k]的最小值除以dp[k + 1][j] 的最大值,其中 i <= k < j
动态规划
- 初始时,给所有 dp[i][i] 的最值都设为索引 i 处的数组值本身
- 从长度 2 开始,至长度 n,逐渐求出整个数组对应的最大值
加括号时需要注意,除法默认的运算顺序是从左至右的,所以分子不需要加括号,只有分母需要加,且题目规定不能有冗余括号,所以仅当分母的元素个数大于 1 时才加括号
代码:
public class OptimalDivision {
public String optimalDivision(int[] nums) {
int n = nums.length;
Node[][] dp = new Node[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = i == j ? new Node(nums[i]) : new Node();
}
}
for (int i = 1; i < n; i++) { // 本轮迭代区间的长度为 i+1
for (int j = 0; j + i < n; j++) { // 本轮迭代区间为 [j, j + i]
for (int k = j; k < j + i; k++) { // 本轮迭代区间的分子分母的界限索引 k
// 更新区间最大值
if (dp[j][j + i].maxVal < dp[j][k].maxVal / dp[k + 1][j + i].minVal) {
dp[j][j + i].maxVal = dp[j][k].maxVal / dp[k + 1][j + i].minVal;
if (k + 1 == j + i) { // 若分母只含一个数,不用加括号
dp[j][j + i].maxStr = dp[j][k].maxStr + "/" + dp[k + 1][j + i].minStr;
} else {
dp[j][j + i].maxStr = dp[j][k].maxStr + "/(" + dp[k + 1][j + i].minStr + ")";
}
}
// 更新区间最小值
if (dp[j][j + i].minVal > dp[j][k].minVal / dp[k + 1][j + i].maxVal) {
dp[j][j + i].minVal = dp[j][k].minVal / dp[k + 1][j + i].maxVal;
if (k + 1 == j + i) { // 若分母只含一个数,不用加括号
dp[j][j + i].minStr = dp[j][k].minStr + "/" + dp[k + 1][j + i].maxStr;
} else {
dp[j][j + i].minStr = dp[j][k].minStr + "/(" + dp[k + 1][j + i].maxStr + ")";
}
}
}
}
}
return dp[0][n - 1].maxStr;
}
// 定义内部类,用来存储对应区间的最值和字符串
class Node {
double maxVal, minVal;
String maxStr, minStr;
public Node() {
maxVal = 0.0;
minVal = 1000.0;
}
public Node(int num) {
maxVal = num;
minVal = num;
maxStr = String.valueOf(num);
minStr = String.valueOf(num);
}
}
}