开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情
竞赛题目链接:leetcode.cn/contest/wee…
A.从数量最多的堆取走礼物
简化题意:一堆数字,重复k次:将最大数替换为其取根号(向下取整),最后的和为多少。
模拟
首先比较直接的思路是我们去按照题意模拟,也就是遍历一遍,找到最大值,将其修改为平方根,重复k次即可,答案就是最后再求和。 时间复杂度O(kn),这里k和n都是10^3,符合要求。
排序
如果想省事,也可以直接调用sort,修改值,再调用sort,重复k次。 时间复杂度O(knlogn),这里k和n都是10^3,符合要求。
堆
这道题是重复地取最大值和放入其平方根。很符合堆排序的场景,建立最大堆,每次取数复杂度O(logn),插入复杂度O(logn),整体复杂度O(klogn)
package main
import (
"container/heap"
"math"
)
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() (v interface{}) {
*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
return
}
func pickGifts(gifts []int, k int) int64 {
h := &IntHeap{}
heap.Init(h)
for i := 0; i < len(gifts); i++ {
heap.Push(h, gifts[i])
}
for i := 0; i < k; i++ {
t := heap.Pop(h)
t = int(math.Sqrt(float64(t.(int))))
heap.Push(h, t)
}
ans := int64(0)
for i := 0; i < len(gifts); i++ {
ans += int64(heap.Pop(h).(int))
}
return ans
}
B.统计范围内的元音字符串数
题意简化:给你字符串数组,k次询问从下标l到r有多少个字符串的首位单词都是元音字母。
枚举
暴力对lr内每个字符串判断。 时间复杂度O(kn),k和n都是10^5,超时。
前缀和
创建一个数组m,记录从0到i有几个字符串符合条件,对lr区间的答案就是m[r]-m[l-1]
时间复杂度O(k)
func vowelStrings(words []string, queries [][]int) []int {
m := make([]int, len(words)+1)
for i := 0; i < len(words); i++ {
if (words[i][0] == 'a' || words[i][0] == 'e' || words[i][0] == 'i' || words[i][0] == 'o' || words[i][0] == 'u') && (words[i][len(words[i])-1] == 'a' || words[i][len(words[i])-1] == 'e' || words[i][len(words[i])-1] == 'i' || words[i][len(words[i])-1] == 'o' || words[i][len(words[i])-1] == 'u') {
m[i+1] = m[i] + 1
} else {
m[i+1] = m[i]
}
}
ans := make([]int, len(queries))
for i := 0; i < len(queries); i++ {
ans[i] = m[queries[i][1]+1] - m[queries[i][0]]
}
return ans
}
C.打家劫舍 IV
题目简化:给你一个数组,你从中取最少k个数(不能取连续两个数),问取到的数的最大值最小是多少。
二分答案
题目很明确地是一个最大值最小化的问题,这类题型通常可以用二分答案的思路。对于取到的数的最大值,在这个数比较小时难以满足,在这个数比较大是可以满足的,也就是满足二分的单调性。对这个最大值进行二分即可得到答案。 时间复杂度O(nlogn)
package main
import "sort"
func judge(nums []int, k, top int) bool {
l := false
ans := 0
for i := 0; i < len(nums); i++ {
if l == true {
l = false
continue
}
if nums[i] <= top {
ans++
l = true
} else {
l = false
}
}
return ans >= k
}
func minCapability(nums []int, k int) int {
cp := make([]int, len(nums))
copy(cp, nums)
sort.Ints(cp)
l, r := cp[0], cp[len(cp)-1]
for l < r {
mid := (l + r) / 2
if !judge(nums, k, mid) {
l = mid + 1
} else {
r = mid
}
}
return l
}
D.重排水果
简化题意:给你两个长度相等的数组,想要这两个数组每个数出现的次数相同,每次可以交换任意两个数,交换的代价为这两个数的最小值,问代价和最小为多少。
题目分析
首先我们要知道哪些数是要交换的:假设对于k在两个数组共出现m次,如果第一个数组的k大于m/2个,那么这些数就是要交换出去的。 很明显当m为奇数不可能成立,直接返回-1.
贪心
有了些待交换的数,如何交换效率最优?
- 贪心的想法:把这个数组待交换的最小值与另一个数组待交换的最大的交换
- 一个特殊情况:可以把非待交换数与待交换数交换,相当于工具人,贪心的思路是直接取最小数交换即可。 时间复杂度O(nlogn)
package main
import (
"math"
"sort"
)
func minCost(basket1 []int, basket2 []int) int64 {
m := make(map[int]int, 100005)
m1 := make(map[int]int, 100005)
m2 := make(map[int]int, 100005)
minn := math.MaxInt
for i := 0; i < len(basket1); i++ {
m[basket1[i]]++
m[basket2[i]]++
m1[basket1[i]]++
m2[basket2[i]]++
}
for k, v := range m {
if v%2 == 1 {
return -1
} else if k < minn {
minn = k
}
}
l1, l2 := make([]struct {
value int
num int
}, 0), make([]struct {
value int
num int
}, 0)
for k, v := range m1 {
if v > m[k]/2 {
l1 = append(l1, struct {
value int
num int
}{value: k, num: v - m[k]/2})
}
}
for k, v := range m2 {
if v > m[k]/2 {
l2 = append(l2, struct {
value int
num int
}{value: k, num: v - m[k]/2})
}
}
sort.Slice(l1, func(i, j int) bool {
return l1[i].value < l1[j].value
})
sort.Slice(l2, func(i, j int) bool {
return l2[i].value < l2[j].value
})
ans := int64(0)
// 情况1
for len(l1) > 0 && len(l2) > 0 {
if l1[0].value <= minn*2 || l2[0].value <= minn*2 {
if l1[0].value < l2[0].value {
num := min(l1[0].num, l2[len(l2)-1].num)
ans += int64(num) * int64(l1[0].value)
l1[0].num -= num
l2[len(l2)-1].num -= num
} else {
num := min(l2[0].num, l1[len(l1)-1].num)
ans += int64(num) * int64(l2[0].value)
l2[0].num -= num
l1[len(l1)-1].num -= num
}
if len(l1) > 0 && l1[0].num == 0 {
l1 = l1[1:]
}
if len(l1) > 0 && l1[len(l1)-1].num == 0 {
l1 = l1[:len(l1)-1]
}
if len(l2) > 0 && l2[0].num == 0 {
l2 = l2[1:]
}
if len(l2) > 0 && l2[len(l2)-1].num == 0 {
l2 = l2[:len(l2)-1]
}
} else {
break
}
}
// 情况2
for len(l1) > 0 {
ans += int64(minn) * int64(l1[0].num)
l1 = l1[1:]
}
for len(l2) > 0 {
ans += int64(minn) * int64(l2[0].num)
l2 = l2[1:]
}
return ans
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
作者本人在最后一题中的特殊情况卡住了,第一发wa之后注意到还有这样的特殊情况,也想到了用非待交换数进行交换,但是脑子一根筋还以为一个数交换后就不用再次交换了,导致最后一题没写出来。好在前三题比较顺利名次对标竞赛积分2100左右