面试题算法:金矿问题-动态规划
- 很久很久以前,有一位国王拥有5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人人数也不同。例如有的金矿储量是500kg黄金,需要5个工人来挖掘;有的金矿储量是200kg黄金,需要3个工人来挖掘…
- 如果参与挖矿的工人的总数是10。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半的金矿。要求用程序求出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?
递归求解
// 时间复杂度O(2^n)
function getBestGold(workers, canSelectGold, needWorkersArray, goldCountArray) {
// 人用完或金矿挖完停止递归
if (workers === 0 || canSelectGold === 0) {
return 0;
}
// 如果
if (workers < needWorkersArray[canSelectGold - 1]) {
return getBestGold(workers, canSelectGold - 1, needWorkersArray, goldCountArray)
}
return Math.max(
getBestGold(workers, canSelectGold - 1, needWorkersArray, goldCountArray),
getBestGold(workers - needWorkersArray[canSelectGold - 1], canSelectGold - 1, needWorkersArray, goldCountArray) + goldCountArray[canSelectGold - 1]
)
}
let workers = 10;// 总人数
let needWorkersArray = [5, 5, 3, 4, 3];// 每个金矿所需人数
let goldCountArray = [400, 500, 200, 300, 350];// 每个金矿储量
// let res = getBestGold(workers, goldCountArray.length, needWorkersArray, goldCountArray);
// console.log(res)
自底向上求解
- 状态转移方程: n是当前金矿储量,w是当前金矿所需人数 ,p是挖掘金矿所需人数数组,g是金矿储量数组
- F(n,w) = F(n-1,w) (n>1,w<p[n-1])
- F(n,w) = max( F(n-1,w),F(n-1,w-p[n-1])+g[n-1]) (n>1,w>=p[n-1])
二维数组表示 ,这个比上一个递归好理解
// 时间复杂度O(nw)
function getBestGold2(workers, needWorks, goldArray) {
let resultArray = []
// 填充二维数组
for (let i = 0; i < goldArray.length + 1; i++) {
resultArray.push([0])
for (let j = 0; j < workers + 1; j++) {
let arr = resultArray[i]
arr.push(0)
}
}
console.log(resultArray)
// 计算每个位置的值
for (let i = 1; i <= goldArray.length; i++) {
for (let j = 1; j <= workers; j++) {
if (j < needWorks[i - 1]) {
resultArray[i][j] = resultArray[i - 1][j]
} else {
resultArray[i][j] = Math.max(resultArray[i - 1][j], resultArray[i - 1][j - needWorks[i - 1]] + goldArray[i - 1])
}
}
}
console.log(resultArray[goldArray.length][workers])
return resultArray[goldArray.length][workers]
}
getBestGold2(workers, needWorkersArray, goldCountArray)// 900
结果如下:
优化:只保存一行最大值
// 时间复杂度O(nw),空间复杂度O(n)
// 只保存一行最大值
function getBestGold3(workers,needWorkers,goldArray) {
let resultArray = new Array(workers+1).fill(0)
console.log(resultArray)
// 计算每个位置的值
// 计算行
for (let i = 1; i <= goldArray.length; i++) {
// 计算列,从右向左,人数多挖的金矿肯定多,所以可以避免很多不必要的计算
// 比如说最后一列大于倒数第二列,那么接下来就不用再计算,直接进入下一行
for (let j = workers; j >=1; j--) {
if (j >= needWorkers[i - 1]) {
resultArray[j] = Math.max(resultArray[j], resultArray[j - needWorkers[i - 1]] + goldArray[i - 1])
}
}
}
console.log(resultArray[workers])
return resultArray[workers]
}
getBestGold3(workers, needWorkersArray, goldCountArray)// 900