饭馆菜品选择问题 | 豆包MarsCode AI刷题

48 阅读2分钟

题目解析

小C来到一家饭馆,这里有 ( n ) 道菜,每道菜有对应的价格和是否含有蘑菇的标记。小C想点 ( k ) 道菜,其中最多允许有 ( m ) 道菜含有蘑菇。她希望在满足上述条件的情况下,总价格尽可能低。如果无法满足条件,则输出 (-1)。


解题思路

这是一道经典的动态规划(DP)问题,目标是在一个限制条件下求最优解。可以通过三维状态数组来求解。以下是解题步骤:

1. 状态定义

定义一个三维 DP 数组 dp[i][j][l],表示:

  • 考虑前 ( i ) 道菜。
  • 已经选择了 ( j ) 道菜。
  • 其中 ( l ) 道菜含有蘑菇。
  • 此时的最小总价格。

2. 状态转移

  • 不选第 ( i ) 道菜: dp[i][j][l]=dp[i1][j][l] dp[i][j][l] = dp[i-1][j][l]

    表示直接继承前 i1i-1 道菜的结果。

  • 选第 ( i ) 道菜:

    • 如果第 ( i ) 道菜含有蘑菇(( s[i-1] = '1' ) 且 ( l > 0 )):
      dpdp[i][j][l]=min(dp[i][j][l],dp[i1][j1][l1]+a[i1])i][j][l] = \min(dp[i][j][l], dp[i-1][j-1][l-1] + a[i-1])

    • 如果第 ( i ) 道菜不含蘑菇(( s[i-1] = '0' )):
      dp[i][j][l]=min(dp[i][j][l],dp[i1][j1][l]+a[i1])dp[i][j][l] = \min(dp[i][j][l], dp[i-1][j-1][l] + a[i-1])

3. 边界条件

  • 初始化 dp 数组,所有状态设为不可达(math.MaxInt32),除了 dp[0][0][0] = 0,表示未选择任何菜时总花费为 0。

4. 结果提取

  • 遍历所有可能的蘑菇数量 ( l ),找到满足 ( dp[n][k][l] ) 的最小值。
  • 如果最小值仍然是初始值,说明无法满足条件,返回 (-1)。

实现代码

package main

import (
	"fmt"
	"math"
)

func solution(s string, a []int, m int, k int) int64 {
	n := len(a)

	// 初始化 dp 数组
	dp := make([][][]int, n+1)
	for i := range dp {
		dp[i] = make([][]int, k+1)
		for j := range dp[i] {
			dp[i][j] = make([]int, m+1)
			for l := range dp[i][j] {
				dp[i][j][l] = math.MaxInt32 // 初始化为不可达
			}
		}
	}

	// 初始状态
	dp[0][0][0] = 0

	// 动态规划
	for i := 1; i <= n; i++ {
		for j := 0; j <= k; j++ {
			for l := 0; l <= m; l++ {
				// 不选择第 i 道菜
				dp[i][j][l] = dp[i-1][j][l]

				// 选择第 i 道菜
				if j > 0 {
					if s[i-1] == '1' && l > 0 { // 第 i 道菜含有蘑菇
						dp[i][j][l] = min(dp[i][j][l], dp[i-1][j-1][l-1]+a[i-1])
					} else if s[i-1] == '0' { // 第 i 道菜不含蘑菇
						dp[i][j][l] = min(dp[i][j][l], dp[i-1][j-1][l]+a[i-1])
					}
				}
			}
		}
	}

	// 提取结果
	res := math.MaxInt32
	for i := 0; i <= m; i++ {
		res = min(res, dp[n][k][i])
	}
	if res == math.MaxInt32 {
		res = -1
	}
	return int64(res)
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

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)
}

复杂度分析

  1. 时间复杂度:

• 三重循环:外层循环遍历 ( n ) 道菜,第二层和第三层分别遍历 ( k ) 和 ( m ) 的限制。

• 总时间复杂度为 O(nkm)O(n \cdot k \cdot m)

  1. 空间复杂度:

• 使用了三维数组 ( dp ),空间复杂度为 O(nkm) O(n \cdot k \cdot m)