每日一题重返战场
题目链接:1962. 移除石子使总数最小
func minStoneSum(piles []int, k int) (ans int) {
h := &hp{piles}
heap.Init(h)
for ; k > 0 && piles[0] != 0; k-- {
piles[0] -= piles[0]/2 // 修改堆顶
heap.Fix(h, 0) // 向下调整
}
for _, x := range piles {
ans += x
}
return ans
}
type hp struct {arr []int}
func (h hp) Swap(i, j int) { h.arr[i], h.arr[j] = h.arr[j], h.arr[i] }
func (h hp) Less(i, j int) bool { return h.arr[i] > h.arr[j] } // 建大堆
func (h hp) Len() int { return len(h.arr)}
func (hp) Pop() (_ any) { return }
func (hp) Push(any) {}
我们可以通过将题目给的数组建成一个大堆,然后通过修改每次修改堆顶,就能实现题目每次修改数组的最大值的要求了,然后修改 k 次,最后再求数组值的和返回即可
想念 C++ 的 priority_queue 了,go 的 heap 使用起来真的是折磨,他需要:
说干就干:
func minStoneSum(piles []int, k int) (ans int) {
n := len(piles)
buildMaxHeap(piles, n)
for ; k > 0 && piles[0] != 0; k-- {
piles[0] -= piles[0]/2 // 修改堆顶
AdjustDown(piles, n, 0) // 向下调整
}
for _, x := range piles {
ans += x
}
return ans
}
func buildMaxHeap(nums []int, n int) { // 建一个大堆
for i := (n-2)/2; i >= 0; i-- { // 不需要遍历叶子节点(如果不明白就画个图)
AdjustDown(nums, n, i)
}
}
func AdjustDown(nums []int, n, parent int) { // 向下调整操作
child := parent*2+1 // (左孩子)子节点 = 父节点 * 2 + 1, 右孩子是 * 2 + 2
for child < n { // 子节点需要在堆(数组)的范围内
if (child+1) < n && nums[child+1] > nums[child] { // 比较左右孩子大小
child++ // 右孩子大, 那就往右孩子那边调整
}
if nums[child] > nums[parent] { // 如果子节点大于父节点, 就交换
swap(&nums[child], &nums[parent]);
parent = child // child 作为新的父节点
child = parent*2+1 // 找下一个孩子
} else {
break // 这部分已经是大堆了
}
}
}
func swap(a, b *int) {
*a, *b = *b, *a
}
舒服了,如果不知道怎么建堆,可以看,数组中的第K个最大元素:建堆的详细解析