由搜索转到01背包
01背包
思路剖析
1.当我们第一次看到题目的时候,很容易想到运用贪心的思想,每次拿性价比最大的物品。
容易举出反例:
3 7 6 10 //1.666... 5 8 // 1.6 2 3 //1.5
2.接着我们很自然的就可以想到,既然运用贪心的思想不行,那么就枚举所有选取物品的情况,也就是组合。
于是我们就可以定义状态
表示我搜索到了第 个物品,此时已经选的物品的总体积为 , 所得到的价值为 。
当我们将 个物品全都搜完后,若 ,则更新我们的答案。
在这个过程中,我们可以发现 , 对于一个状态 ,它可以从 或 转移过来(分别对应了我选不选第 个物品的情况),但由于他所剩的物品个数一样,已经选的体积一样,所以尽管从两个不同的状态转移过来,所得的价值不同,但是之后能够遍历到的情况都是相同的。
显然有所得价值更大的状态,得到的结果优于价值更小的状态。
这就是最优子结构性质,正因为这个过程具有最优子结构性质,我们在进行下一阶段计算的时候,就免去了绝对不可能是最优的答案的多余的计算,所以我们可以通过他将搜索转化为DP,当然,这过程中同时具无后效性原则和重叠子问题。
简单解释一下重叠子问题优化的部分。当我已经知道状态 所能获得的最大价值时,我将它记录下来,之后再遍历到这个状态,我直接就能返回我的最优解了,而不用继续搜下去,重新求解。这就要求了我们DP的问题具有该性质,否则就等同于没有记录,也就没有优化一说了。
无后效性则保证了我求出来的最优解不会被后面所影响,一定是最优的。
3.简单总结一下
状态: 表示以在前 个物品中,选取体积为 的物品,得到的最大价值。
状态转移方程:
code
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int n,m,v[3500],w[3500],dp[12885]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]); memset(dp,0xcf,sizeof(dp));//赋值为-INF,表示该状态不可达,这里要特别注意,不能赋值为0,如果赋值为0的话,则表示容积为j的背包所能获得的最大价值,与我们所定义的状态相悖。 dp[0]=0; for(int i=1;i<=n;i++){ for(int j=m;j>=v[i];j--){ dp[j]=max(dp[j],dp[j-v[i]]+w[i]); } } int ans=0; for(int i=1;i<=m;i++) ans=max(ans,dp[i]);//找到最佳答案。 cout<<ans<<endl; return 0; }
唔,当然,如若是01背包的话,用 表示前 个物品中,用容积为 的背包所能获得的最大价值也是ok的,但如果题目要我们恰好要把容积为 的背包装满,这个状态所得到的答案就不一定正确了。
我给出的代码是已经优化过的版本,具体过程相信很多大佬都比我讲得好,这里主要分享我的一个思路,希望可以帮助自己巩固复习,也可以帮到大家理解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!