01背包问题回溯法_回溯法解决01背包问题时间复杂度

01背包问题回溯法_回溯法解决01背包问题时间复杂度背景0-1背包是非常经典的算法问题,很多场景都可以抽象成这个问题模型。这个问题的经典解法是动态规划。不过还有一种简单但没有那么高效的解法,这里用的回溯算法。0-1背包问题有很多变体,我这里介绍一种比较基础的。我们有一个背包,背包总的承载重量是Wkg。现在我们有n个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

背景

  • 0-1背包是非常经典的算法问题,很多场景都可以抽象成这个问题模型。这个问题的经典解法是动态规划。
  • 不过还有一种简单但没有那么高效的解法,这里用的回溯算法。
  • 0-1背包问题有很多变体,我这里介绍一种比较基础的。我们有一个背包,背包总的承载重量是Wkg。现在我们有n个物品,每个物品的重量不等,并且不可分割。 我们现在期望选择几件物品,装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大?
  • 实际上,假设物品是不可分割的,要么装要么不装,所以叫0-1背包问题。显然,这个问题已经无法通过贪心算法来解决了。我们现在来看看,用回溯算法如何来解决。
  • 对于每个物品来说,都有两种选择,装进背包或者不装进背包。对于n个物品来说,总的装法就有2^n种,去掉总重量超过Wkg的,从剩下的装法中选择总重量最 接近Wkg的。不过,我们如何才能不重复地穷举出这2^n种装法呢?这里就可以用回溯的方法。

我们可以把物品依次排列,整个问题就分解为了n个阶段,每个阶段对应一个物品怎么选择。先对第一个物品进行处理,选择装进去或 者不装进去,然后再递归地处理剩下的物品。描述起来很费劲,我们直接看代码,反而会更加清晰一些。

代码如下

package main

import "fmt"

//总的思想是,把n个物体依次排列
//对每个当前物体考虑两种情况,一种是不放进入背包后,递归遍历后边的各种情况
//另一种是放进入背包后,再递归遍历后边的各种情况
//出递归条件,已经访问了最后一个物体了或者已经是背包最大容量了

var maxW int                              //表示背包能装的最大重量
var curAnswer []int                       //当前背包状态,下标表示物体,值表示是否装进背包
var bestAnswerMap = make(map[int][][]int) //存储最佳答案,key背包重量,值为多种情况,所以为数组

func f(i, cw int, items []int, n, w int) {
	if curAnswer == nil { //其实只初始化了一次
		curAnswer = make([]int, n) //curAnswer[x]值为1,表示x加入背包
	}
	if cw == w || i == n {
		if cw != 0 && cw >= maxW { //cw是在背包允许的重量范围里的值,取其中最大值
			maxW = cw
			//如下是为了打印出最佳结果
			bst := make([]int, n)
			for j := 0; j < n; j++ {
				bst[j] = curAnswer[j]
			}
			if val, ok := bestAnswerMap[maxW]; ok {
				val = append(val, bst)
				bestAnswerMap[maxW] = val
				//map中的元素是不能寻址的,val,ok这种方式,拿到的是副本
			} else {
				temp := make([][]int, 0)
				temp = append(temp, bst)
				bestAnswerMap[maxW] = temp
			}
			//以上是为了打印出最佳结果
		}
		return
	}
	curAnswer[i] = 0        //考虑不放进入
	f(i+1, cw, items, n, w) //考虑不放入当前物体的情况
	if cw+items[i] <= w {
		curAnswer[i] = 1                 //考虑放进入背包
		f(i+1, cw+items[i], items, n, w) //考虑放入当前物体的情况
	}
}

func main() {
	a := []int{1, 2, 3, 4, 10, 5, 6, 7, 8, 9}
	f(0, 0, a, 10, 10)
	fmt.Println(maxW)
	for k, v := range bestAnswerMap {
		fmt.Printf("key is %d, value is %v\n", k, v)
		fmt.Println("=======================")
	}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/184329.html原文链接:https://javaforall.net

(0)
上一篇 2022年10月8日 下午10:46
下一篇 2022年10月8日 下午11:00


相关推荐

  • CSS 背景位置 background-position属性

    CSS 背景位置 background-position属性背景位置除了背景背景平铺外 CSS 还提供了另一个强大的功能 即背景定位技术 能够精确控制背景在对象中的位置 默认情况下 背景图像都是从元素 padding 区域的左上角开始出现的 但设计师往往希望背景能够出现在任何位置 通过 background position 属性 可以很轻松的控制背景图像在对象的背景区域中的起始显示位置 其语法格式为 background position xposypo

    2026年3月17日
    2
  • stm32看门狗工作原理

    stm32看门狗工作原理一、为什么需要看门狗?在MCU微型计算机系统中,MCU运行程序很容易受到外界电磁场的干扰,从而造成程序运行错误甚至造成跑飞现象,从而陷入死循环,程序的正常运行被打乱,从而造成不可预料的严重后果,于是人们就设计了一款用于实时监测计算机运行状态的芯片,我们就将其称为看门狗。二、看门狗的作用看门狗会无时无刻的去喂狗,如果程序发生跑飞的现象,那么单片机程序就不能继续去喂狗,继而计数器计数到0,看门狗产生复位脉冲信号。单片机发生程序跑飞的现象才会产生一个复位脉冲强制单片机复位,从而使单片机正常运行。三、看门狗

    2022年6月9日
    24
  • Claude Code 添加 MCP 服务器完整指南:从入门到精通(2025最新)

    Claude Code 添加 MCP 服务器完整指南:从入门到精通(2025最新)

    2026年3月16日
    2
  • intellij idea 打war包 配置

    intellij idea 打war包 配置选择 for 对应的 exploded 这样就能打 war

    2026年3月18日
    2
  • QDir中mkdir和mkpath的区别

    QDir中mkdir和mkpath的区别QDir 中 mkdir 和 mkpath 的区别 mkdir 原型 boolQDir mkdir constQString amp dirName constCreates directorycal Returnstrueo otherwiseret Ifthedirecto

    2026年1月26日
    4
  • Vue项目关闭eslint校验「建议收藏」

    Vue项目关闭eslint校验「建议收藏」1.vue-cli2.0关闭eslint校验vue-cli2.0实现2.vue-cli3.0关闭eslint校验报错:eslint-disable-next-linetoignorethenextline.解决方法:找到文件vue.config.js,打开文件:修改lintOnSave为false,如果没有就添加lintOnSave为false…

    2022年5月15日
    53

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号