饭馆菜品选择问题
问题描述
小C来到了一家饭馆,这里共有 n 道菜,第 i 道菜的价格为 a_i。其中一些菜中含有蘑菇,s_i 代表第 i 道菜是否含有蘑菇。如果 s_i = '1',那么第 i 道菜含有蘑菇,否则没有。
小C希望点 k 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有 m 道菜含有蘑菇。小C想知道在满足条件的情况下能选出的最小总价格是多少。如果无法按照要求选择菜品,则输出-1。
测试样例
样例1
输入:s = "001", a = [10, 20, 30], m = 1, k = 2
输出:30
样例2
输入:s = "111", a = [10, 20, 30], m = 1, k = 2
输出:-1
样例3
输入:s = "0101", a = [5, 15, 10, 20], m = 2, k = 3
输出:30
算法设计
- 预处理:首先计算字符串
s中含有蘑菇的菜品数量count。 - 可行性检查:如果菜品总数小于
k或者即使全部替换掉蘑菇也无法达到k元,则提前返回-1。 - 排序:为了尽可能少地花费,先对价格列表
a进行升序排列。 - 选择菜品:遍历排序后的价格列表,优先选择不含蘑菇的菜品,直到总价达到
k或者无法再增加总价为止。
复杂度分析
时间复杂度分析
-
初始化:
- 将字符串
s转换为[]rune,时间复杂度为 (O(n)),其中 (n) 是字符串s的长度。 - 计算含有蘑菇的菜品数量
count,需要遍历字符串s,时间复杂度为 (O(n))。
- 将字符串
-
可行性检查:
- 检查
n < k和n - count + m < k,这些操作的时间复杂度为 (O(1))。
- 检查
-
排序:
- 使用
sort.Ints(a)对价格数组a进行升序排序。Go 标准库中的sort.Ints使用的是快速排序算法(在某些情况下退化为堆排序),其平均时间复杂度为 (O(n \log n))。
- 使用
-
选择菜品:
- 遍历排序后的数组
a,进行选择菜品的操作。最坏情况下需要遍历整个数组,时间复杂度为 (O(n))。
- 遍历排序后的数组
综上所述,总的时间复杂度由排序步骤决定,即 (O(n \log n))。
空间复杂度分析
-
字符串转换:
- 将字符串
s转换为[]rune,空间复杂度为 (O(n))。
- 将字符串
-
其他变量:
- 除了
ch之外,其他变量如count,sum,m,k等都是常数级别的额外空间,空间复杂度为 (O(1))。
- 除了
-
排序:
- Go 标准库中的
sort.Ints在原地排序,不需要额外的空间,空间复杂度为 (O(1))。
- Go 标准库中的
综上所述,总的空间复杂度为 (O(n))。
代码
package main
import (
"fmt"
"sort"
)
func solution(s string, a []int, m int, k int) int64 {
n := len(a)
ch := []rune(s)
count := 0
for i := 0; i < n; i++ {
if ch[i] == '1' {
count++
}
}
if n < k {
return -1
}
if n-count+m < k {
return -1
}
sum := 0
sort.Ints(a) // 直接使用内置排序函数代替冒泡排序
for i := 0; i < n; i++ {
if ch[i] == '1' {
if m > 0 {
sum += a[i]
k--
m--
if k == 0 {
break
}
continue
}
} else {
sum += a[i]
k--
if k == 0 {
break
}
}
}
return int64(sum)
}
测试
func main() {
fmt.Println(solution("001", []int{10, 20, 30}, 1, 2) == 30)
fmt.Println(solution("111", []int{10, 20, 30}, 1, 2) == -1)
fmt.Println(solution("0101", []int{5, 15, 10, 20}, 2, 3) == 30)
}