1.单词拆分
func wordBreak(s string, wordDict []string) bool {
n := len(s)
dp := make([]bool, n+1)
dp[0] = true
for j := 0; j < len(wordDict); j++ {
if len(wordDict[j]) > n {
continue
}
if wordDict[j] == s[:len(wordDict[j])] {
dp[len(wordDict[j])] = true
}
}
//fmt.Println(dp)
for i := 1; i < n; i++ {
//fmt.Println(dp)
if !dp[i] { //表明不能从当前位置开始续接这个字符串
continue
}
for j := 0; j < len(wordDict); j++ {
if len(wordDict[j])+i > n {
continue
}
if wordDict[j] == s[i:len(wordDict[j])+i] {
dp[len(wordDict[j])+i] = true
if dp[n] {
return dp[n]
}
}
}
}
return dp[n]
}
2.多重背包理论基础
2.1 什么是多重背包问题
有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。
2.2 将多重背包展开为01背包
package main
import "fmt"
func main() {
var C, N int
fmt.Scanln(&C, &N)
//C, N := 10, 3
w := make([]int, N)
v := make([]int, N)
k := make([]int, N)
for i := 0; i < N; i++ {
fmt.Scan(&w[i])
}
for i := 0; i < N; i++ {
fmt.Scan(&v[i])
}
for i := 0; i < N; i++ {
fmt.Scan(&k[i])
}
//w := []int{1, 3, 4}
//v := []int{15, 20, 30}
//k := []int{2, 3, 2}
sum := 0
for i := 0; i < N; i++ {
sum += k[i]
}
real_w := make([]int, sum)
real_v := make([]int, sum)
index := 0
for i := 0; i < N; i++ {
for j := 0; j < k[i]; j++ {
real_w[index] = w[i]
real_v[index] = v[i]
index++
}
}
//fmt.Println(real_w)
dp := make([]int, C+1)
for j := 0; j < sum; j++ { //物品种类
for i := C; i >= real_w[j]; i-- { //背包大小
if i-real_w[j] >= 0 {
dp[i] = max(dp[i], dp[i-real_w[j]]+real_v[j])
}
}
//fmt.Println(dp)
}
fmt.Println(dp[C])
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
2.3 优雅的写法
package main
import "fmt"
func main() {
var C, N int
fmt.Scanln(&C, &N)
//C, N := 10, 3
w := make([]int, N)
v := make([]int, N)
nums := make([]int, N)
for i := 0; i < N; i++ {
fmt.Scan(&w[i])
}
for i := 0; i < N; i++ {
fmt.Scan(&v[i])
}
for i := 0; i < N; i++ {
fmt.Scan(&nums[i])
}
dp := make([]int, C+1)
for i := 0; i < N; i++ { //遍历物品
for j := C; j >= w[i]; j-- { //遍历背包容量
// 以上为01背包,然后加一个遍历个数
for k := 1; k <= nums[i] && (j-k*w[i] >= 0); k++ {
dp[j] = max(dp[j], dp[j-k*w[i]]+k*v[i])
}
}
}
fmt.Println(dp[C])
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
3. 背包问题总结篇
常用的五部走法:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
常见的类型
- 01背包
- 一维dp数组的背包在遍历顺序上和二维dp数组实现的01背包其实是有很大差异的,大家需要注意!
- 完全背包
- 如果求组合数就是外层for循环遍历物品,内层for遍历背包。
- 如果求排列数就是外层for遍历背包,内层for循环遍历物品。