问:
- 给定一个整数数组,其中可能有正有负有0,求子数组的最大异或和
- 戳气球
- 汉诺塔问题中,假设一个数组arr=['右','右','中','左'],其含义为第i个圆盘在第arr[i]柱上。那么请返回这个arr代表的圆盘状态在汉诺塔最优运行策略中是第几步达到的。如果不是汉诺塔最优运行策略中的某一状态则返回-1。
解:
- 暴力解,遍历数组,以arr[i]作为子数组的结尾时,再遍历0~i,以arr[j]作为子数组的开头时,每一种情况都异或一遍,找到最大情况。
// 暴力解
function getMaxEor(arr) {
if (!arr.length) return 0
let eorI = 0
let maxEor = 0
// 以arr[i]结尾
for (let i = 0; i < arr.length; i++) {
// 0 ~ i的异或和
eorI ^= arr[i]
// 以arr[j]开头
for (let j = 0; j <= i; j++) {
let eorPre = 0
for (let k = 0; k <= j - 1; k++) {
// 0 ~ j - 1的异或和
eorPre ^= arr[k]
}
// [j...i] 的异或和 = [0..i]的异或和 ^ [j-1...i]的异或和
const curEor = eorI ^ eorPre
maxEor = Math.max(maxEor, curEor)
}
}
return maxEor
}
// 预处理生成前缀异或和数组。省去第三层遍历
function getMaxEor(arr) {
if (!arr.length) return 0
let maxEor = 0
const preEor = []
for (let i = 0; i < arr.length; i++) {
preEor[i] = (preEor[i - 1] ?? 0) ^ arr[i]
}
// 以arr[i]结尾
for (let i = 0; i < arr.length; i++) {
// 以arr[j]开头
for (let j = 0; j <= i; j++) {
// j ~ i 的异或和 = 0~i的异或和 ^ j-1~i的异或和
const needEor = preEor[i] ^ (preEor[j - 1] ?? 0)
maxEor = Math.max(maxEor, needEor)
}
}
return maxEor
}
// TODO 前缀树解法。比较麻烦以后再写
// 范围上尝试模型,尝试left~right范围上,每一个位置最后打爆的情况。
// 暴力递归
function maxCoins(arr) {
function getRes(left, right) {
if (left === right) return (arr[left - 1] ?? 1) * arr[left] * (arr[right + 1] ?? 1)
if (left > right) return 0
let res = -Infinity
for (let i = left; i <= right; i++) {
let psb
if (i === left) {
psb = (arr[left -1] ?? 1 * arr[i] * (arr[right + 1] ?? 1)) + getRes(left + 1, right)
} else if (i === right) {
psb = (arr[left -1] ?? 1 * arr[i] * (arr[right + 1] ?? 1)) + getRes(left, right - 1)
} else {
psb = getRes(left, i - 1) + getRes(i + 1, right) + (arr[left -1] ?? 1 * arr[i] * (arr[right + 1] ?? 1))
}
res = Math.max(res, psb)
}
return res
}
return getRes(0, arr.length - 1)
}
// 递归改dp
function maxCoins(arr) {
const dp = []
for (let i = 0; i < arr.length; i++) {
dp[i] = []
for (let j = 0; j < arr.length; j++) {
if (i === j) {
dp[i][j] = (arr[i - 1] ?? 1) * arr[i] * (arr[j + 1] ?? 1)
}
if (i > j) {
dp[i][j] = 0
}
}
}
for (let i = arr.length - 2; i >= 0; i--) {
for (let j = i + 1; j < arr.length; j++) {
dp[i][j] = -Infinity
for (let k = i; k <= j; k++) {
let pro
if (k === i) {
pro = ((arr[i -1] ?? 1) * arr[k] * (arr[j + 1] ?? 1)) + dp[i + 1][j]
} else if (k === j) {
pro = ((arr[i - 1] ?? 1) * arr[k] * (arr[j + 1] ?? 1)) + dp[i][j - 1]
} else {
pro = dp[i][k - 1] + dp[k + 1][j] + ((arr[i - 1] ?? 1) * arr[k] * (arr[j + 1] ?? 1))
}
dp[i][j] = Math.max(dp[i][j], pro)
}
}
}
return dp[0][arr.length - 1]
}
function getStep(arr) {
function getRes(n, from, to, other) {
if (n === -1) return 0
// 判断第n个盘子现在在哪
// 最优轨迹中没有需要 当前圆盘 去other的时候
if (arr[n] === other) return -1
if (arr[n] === from) {
// 代表把 前n-1 个圆盘全部挪到 other 上的过程还没走完,所以去看前n-1挪到哪了
return getRes(n - 1, from, other, to)
}
if (arr[n] === to) {
// 代表已经把第 n 个圆盘挪到to上了
// 继续去看 第n-1个盘子的情况
const preStep = getRes(n - 1, other, to, from)
if (preStep === -1) return -1
// 汉诺塔问题中 第 n 个盘 走完需要 2的n次方步。
return 2 ** (n) + preStep
}
}
return getRes(arr.length - 1,'左', '右', '中')
}