背包九讲01-01背包问题阅读笔记

1.问题描述:

背包问题:
给出n件物品以及我们的背包的总容量v,以及n件物品的价值value和耗费cost,每件物品只有两种选择,装或者不装,请问如何选择装填的物品才可以使我们的最后获得价值最大

2.求解思路:

这我就不阐明记忆化搜索的思路了,既然是背包问题的专题,我就只讲动态规划的方法
定义状态:
dp[i][j]:在容量为j的时候我们选择前i件物品可以获得的最大的价值
状态转移方程:
if j>=cost[i]   dp[i][j]=max(dp[i-1][j],dp[i-1][j-cost[i]]+value[i])
else dp[i][j] = dp[i-1][j]

核心代码段:
for(int i=1;i<=n;i++)   //遍历所有的i件物品
{
	for(int j=0;j<=v;j++)   //遍历背包的所有的状态,从0开始是为了考虑进去耗费是0的物品的状态 
	{
		if(j>=cost[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-cost[i]]+value[i]); 
	}
} 



上述的状态转移方程的含义是这样的:
当我们的目前的背包的容量大于我们当前第i件物品的时候,我们就存在两种决策,取或者不取,作为动态规划来说,我们每次都是选取最优的解,由最优的解推导出下一个状态的最优的解,所以说,这里我们就要选取两种情况的额最优的情况dp[i-1][j]代表本次该物品不取,dp[i-1][j-cost[i]]+value[i]代表本次该物品取

但是当我们的背包的容量不足以容许我们选取第i件物品的时候,我们就只有不取这一种选择,显而易见,也是当前最优的一种选择

3.优化空间复杂度:

如果采用搜索算法来解决这道题的话,时间复杂度会是2^n,毕竟有 2^n这么多的选择,但是我们利用动态规划成功 的将指数时间复杂度优化到了n*v
时间复杂度上,我们已经无法在优化了,但是我们对于空间复杂度却是可以考虑进行优化调整

我们来考虑一下我们的空间耗费和我们的01背包问题的算法步骤
首先:空间耗费是O(n*v)
其次:我们的算法过程:
我们每次都是从之前的记录下来的状态作为我们的表的记录值,通过O(1)的时间复杂度从而完成我们的优化工作
但是,我们可以清楚地发现,我们并不需要之前所有的状态,我们需要的仅仅只是上一次的状态,即i只需要第i-1的状态值
所以说,我们的优化思路已经出具眉目了

我们可以将状态压缩到一维状态下,然后我们通过不断的覆盖从而使下将大的我们实际上不需要的过早记录的值忽略掉
但是这里面牵扯到我们要保存好我们的状态的方法:
之前我们的内层循环是从0循环到v   。 但是在这里我们必须要装换思路,因为我们始终利用之前的状态
如果我们的内层循环还是从0循环到v的话,那么我们会发现我们之前的值已经被新的计算的值覆盖了,也就是说,我们之后的计算如果要利用之前的额记录,这个记录是个错误的值,因为正确的记录应经被我们覆盖了
所以说,我们的小技巧就是从v循环到0,保证我们每次的状态转移的时候都可以利用到我们的争取的dp记录状态

核心代码如下:
for(int i=1;i<=n;i++)   //遍历所有的i件物品
{
	for(int j=v;j>=0;j--)   //遍历背包的所有的状态,从0开始是为了考虑进去耗费是0的物品的状态 
	{
		if(j>=cost[i]) dp[j]=max(dp[j],dp[j-cost[i]]+value[i]); //dp[j]代表的含义是原先的dp[i-1][j],我们将i状态和i-1状态合并到了以为数组中 
	}
} 

4.进一步优化:

首先我们会发现,对于每一次的循环,因为每一件物品的cost值是不一样的,但是我们都是统一从状态v遍历到0(目的是不会遗漏掉所有的记录的状态)
但是我们如果发现,对于任一个状态i来说,我们的背包容量在小于cost[i]的时候是不会发生改变的,我们直接从v循环到cost[i]就可以了,但是每一个物品的cost都不一样,我们该怎么解决这个问题呢
位了进一步优化,我们就引进了函数调用——在原背包九讲中,称之为黑箱状态
不说了,上代码:
void pack01(int cost,int value)  //当然这里也就自然而然的考虑了cost位0的特殊情况 
{
	for(int i=v;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+value);
} 

void algorithm(int cost[],int value[])
{
	for(int i=1;i<=n;i++) pack01(cost[i],value[i]); 
}

5.初始化的问题:(变体问题的好的解决思路)

在我们做01背包问题的时候,我们经常会遇见题目变了一些花样,比如说,恰好装满的最大价值这类的问题
面对这些问题的时候,我们只需要对我们的初始化的时候做一些必要的处理就好了

1.在朴素的问题中,我们全部都初始化位0,这其实代表了一种合法的状态,该合法状态(针对题目的要求限制下的合法状态)的含义就是背包中选择不装任何的东西,那么价值为0,也代表合法

2.在恰好装满的问题中,我们dp[0]=0,代表我们的容量是0的时候恰好装满了,价值是0,合法状态也是0,但是其他的dp状态我们赋予 -inf的状态,表示不合法(在恰好装满的约束条件下,只要有空间但是没有装满的话就是不和题意的不合法状态)

这里面的思想就是,如果子问题合法并且当前的的装填也属于合法(恰好的完全装填)才是合法的状态
否则我们都处理成非法的状态(这里的inf可以参与运算,反正是无穷小,再怎么运算都一样,只是为了让代码更加简洁)

6.最优方案记录:

我们如果要输出最优的额方案的话,有这么两种思路
1.直接利用我们的二位dp矩阵,从头开始往前推
2.额外开辟新的path数组记录路径,其实主要的思路和上面的是一样的,都是逆推回去,每次如果出现了当前第i行的最优解,我们标记一次,然后从后向前遍历,找到第一个标记点然后逐层向前推(推回去点不一定是标记为1的点,我们再以该点为基础向前推找最先出现的标记点,知道找到第1行为止)


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值