Hello, 大家好 好久都没更新算法题目的解题思路了,最近也开始重拾算法。所以要继续更这一块内容了。
今天做的题目的1049,最后的一块石头。具体的题目的内容就不在这边详细说了。大家可以到leetcode官网去看这条题目的描述这边直接上解题的思路
我在刚看到这条题目的时候,我自己也不太懂应该从哪里入手,直到我看到了别人用01背包的解法。我觉得这一题也是可以用此公式直接可以解题出来的
题目简述
Input: stones = [2,7,4,1,8,1] Output: 1 Explanation:
We can combine 2 and 4 to get 2, so the array converts to [2,7,1,8,1] then,
we can combine 7 and 8 to get 1, so the array converts to [2,1,1,1] then,
we can combine 2 and 1 to get 1, so the array converts to [1,1,1] then,
we can combine 1 and 1 to get 0, so the array converts to [1], then that's the optimal value.
题目描述好像很简单,就是输入一个数组,这些数组里面是一些石头的重量。 每次取2个石头。 有2个例子
- 例子1. 石头重量x = 石头重量y 的时候 则抵消该重量
- 例子2. 石头重量x < 石头重量y 的时候 则取 y-x
这个思路是不是稍微就清晰了一些了? 如果有n块石头,总重量为r 分为2组,每组的重量都是r/2。
那么我们只需要创建1个背包 这个背包的容量 就是 r/2 然后我们看看在r/2的容量下最多能放多少重量的石头。
01背包解法
我们都知道背包有 重量 和 价值 之分。那么在这道题目里面 我们的重量和价值是什么?
其实weight[i] 和 value[i] 都是stones[i] 也就是石头的重量是其重量,也是其价值。
接下来我们把公式套上来
dp[j] = max(dp[j], dp[j-weight[i] + value[i])
这边备注一下 如果不知道 这个公式怎么推导出来的,可以参考这篇文章,我觉得是把01背包问题讲得非常透彻的一篇「代码随想录」帮你把0-1背包学个通透! 强烈推荐大家先阅读这一篇
这个就是我们动态规划用1维数组保存的值。此处的dp[j] 代表容量为j时,能存放dp[j] 的价值
接下来就是写代码的事了~
代码
func lastStoneWeightII(stones []int) int {
sum := 0
target := 0
for _, v := range(stones) {
sum += v
}
target = sum / 2 // 此处会向下取整
dp := []int{}
for i:=0; i <= target; i++ {
dp = append(dp, 0) // 初始化数组为全0先
}
for i:= 0; i<len(stones); i++ {
for j := target; j >= stones[i]; j-- {
dp[j] = max(dp[j], dp[j-stones[i]] + stones[i])
}
}
return sum - (dp[target] * 2)
// 整体的思路:
// 看到题目的时候,其实可以判断为 该题目想把数组中的石头平分为2部分,
// 例子1. 石头重量x = 石头重量y 的时候 则抵消该重量
// 例子2. 石头重量x < 石头重量y 的时候 则取 y-x
// 那就很好办了。 只需要知道背包容量为一半的时候 能装下多少 石头。 再把 总数减去2次一半的值。
// 就是最后的答案了。
}
func max (x, y int) int {
if x > y {
return x
}
return y
}
其实代码也没有特别复杂, 需要几个地方讲解一下的就是 最后那个位置
sum-dp[target] - dp[target]
这里是为什么? 我们都知道 sum 如果是奇数那么 除以2以后得出的肯定是有小数点, 因此这边的target是会向下取整。 比如说sum=15 / 2 = 7.5 这时候 target就是7
因此sum-dp[target] > dp[target]
因此只需要求出 sum - dp[target] * 2 的值就可以了