【代码随想录——动态规划——第五周】

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循环遍历物品。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值