01背猫问题
法外狂徒小谢,准备去偷隔壁小姐姐家的三只猫。三只猫重量不同价值不同。但贫穷的小谢只有一只不是非常牢固容量有限的猫包。那么他该如何选取,使得偷盗的价值最高嘞?🤭
我的解题思路
-
明确方向,偷一次不容易,肯定要偷的回本,所以要偷价值最高的东西。但是能力有限,包只有这么大,因此要尽可能的利用好每一寸土地。
-
N = 3 小姐姐家的三只猫
wt = [2,1,3] 每只猫的重量
v = [4,2,3] 每只猫的价值
B = 4 猫包的最大承受重量
开一个二位数组,来解最优偷法,f(k,w) (k = 可选猫的数量, w = 猫包还可以装几斤的猫)
第一次选择:可选3只猫,猫包容量为4 f(3,4)。如果选择了1号猫(质量为2,价值为4),那么包的最终价值Bv = f(3-1,4-2) + 4。继续分解f(2,2)。如果我不选1号,那么我的f(2-1,4)。
第二次选择:可选2只猫,猫包容量为2 f(2,2)。如果选贼了2号猫猫头,猫包的最终价值Bv = f(1,1) + 2 + 4。如果我不选2号猫猫,那么我的f(2-1,2).
第三次选择:可选一只猫,猫包容量为 f(1,1)。此时发现第三支猫重3,要不起,那么f(1,1) = 0 Bv = 0 + 2 + 4 = 6
问题来了❓
Bv = 6 是偷的最划算的吗?解析以下步骤:
有没有什么规律呢?🎈
会发现有两个问题,一个是选不选,一个是拿不拿的了(背包容量够吗) 我们的目标是要拿最多的东西,所以在选择时就要拿最大的。可以总结出以下方程:
f(k,w):k件物品,w包的容量,f(k,w)能偷到的最大价值。
总结容量不同的情况
| 0 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 4 | 4 | 4 |
| 2 | 0 | 2 | 4 | 6 | 6 |
| 3 | 0 | 2 | 4 | 6 | 6 |
我们要的结果在表[3][4]位置
代码
var maxCat = function(w,v,b) {
totalCat = w.length;
let dp = new Array(totalCat+1).fill(0).map(item => new Array(b+1).fill(0))
for(i = 0;i<=totalCat;i++){
for(j = 0; j<=b ;j++){
if(i=== 0 || j===0 ){
dp[i][j] = 0
}else{
if(w[i-1]>j){ //第i件装不下
dp[i][j] = dp[i-1][j]
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1])
}
}
}
}
return dp[totalCat][b];
};
多少种XX问题
多少条路径从A--->B
我的解题思路
-
明确方向:我们的目的是从左上角走到右下角,并且一次只能向下或者向右移动一步
-
观察特殊,我们分析可得,第一行和第一列的值是固定的,也就是前一项+1(上一项+1)
-
列表 | start(1) | 1 | 2 | 3 | 4 | 5 | 6 | n-1 | | --- | --- | --- | --- | --- | --- | --- | --- | | 2 | [0][1]+[1][0] | / | / | /| / | / | / | | 3 | / | [1][2]+[2][1] | / | /| / | / | [m-2][n-1] | | m-1 | / | / | / | /| / | [m-1][n-2] | end |
-
我们想要走到end,根据题意必须要走过n-1或者m-1。若因此end = (m-1)+(n-1)。如上表。
-
我们可以得出中间部分的解 sumgrid[i][j] = sumgrid[i-1][j]+sumgrid[i][j-1]。
-
题目中的解即为sumgrid[m-1][n-1]
var uniquePaths = function(m, n) {
//创建一个二维数组,填充1
let sumgrid = new Array(m).fill(1).map(item => new Array(n).fill(1))
sumgrid[0][0] = 1
for(i = 0; i < m; i++){
if(i === 0){
//只能往右走到达,第一行全部为1
for(j = 1; j < n; j++){
sumgrid[i][j] = 1
}
}else{
//只能往下走到达,第一列全部为1
for(j = 0; j < n; j++){
if(j === 0){
sumgrid[i][j] =1
}else{
sumgrid[i][j] = sumgrid[i-1][j]+sumgrid[i][j-1]
}
}
}
}
return sumgrid[m-1][n-1]
};
结果
优化(减少无用数据)只保留上一行的数据(简化为一维数组问题)
| 1 | 1 | 1 | 1 | 1 | 1 | 1 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|
| 1 | 3 | 6 | 10 | 15 | 21 | 28 |
var uniquePaths = function(m, n) {
let sumgrid = new Array(n).fill(1)
for(i = 1; i < m; i++){
sumgrid[0] = 1
for(j = 1; j < n; j++){
sumgrid[j] = sumgrid[j-1]+sumgrid[j]
}
}
return sumgrid[n-1]
};
结果
最优解问题
最多,最少,最快...
我的解题思路
-
明确目标:走过的路,路径和最小,只能向下或者向右移动一步,输出的是路径和
-
❌一开始使用反推的方法,最后一个格子,只可能是来自上或者左,所以我两者中使用Math.min(a,b)来取较小值,例子中,我发现我有两个2了,那我要取哪个...我瞬间就不理解了。
-
✔本质上,题目是需要我们算出走到每个格子所需要的最少路径,因此我们可以新建一个二维数组,记录每一个格子的最少路径值,该数组的最后一项即为题目的答案
-
实现
- 第一行, 最小的走法是: 一个挨着一个往右, 每个格子的最小值就是grid第一行的累加和。
- 第一列, 最小的走法是: 一个挨着一个往下, 每个格子的最小值就是grid第一列的累加和。
- 一般位置, 要么从左边来, 要么从上边来, 哪个小选择从哪边来。
代码
var minPathSum = function (grid) {
//取行
let m = grid.length
//取列
let n = grid[0].length
//二位数组变为0
let sumgrid = new Array(m).fill(0).map(item => new Array(n).fill(0))
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
//对于第一排的,只能从左边加过来
if (i == 0) {
if (j == 0) {
sumgrid[0][0] =grid[0][0]
continue
}else{
sumgrid[0][j]=sumgrid[0][j-1]+grid[0][j]
continue
}
}
//对于第一列的,只能从上边加过来
if(j==0){
sumgrid[i][0]=sumgrid[i-1][0]+grid[i][0]
continue
}
//对于其他的,找上面或者左边两者之间的更小的一个与自己相加
sumgrid[i][j]=Math.min(sumgrid[i-1][j],sumgrid[i][j-1])+grid[i][j]
}
}
// 最小值就是sum二维度数组中sum[m-1][n-1]的值
return sumgrid[m-1][n-1]
};