背包问题的初步认识

116 阅读2分钟

现在是2025年10月15日晚上,不知不觉大二上学期已经来到了第7周,可是我算法的学习越来越迷茫了,渐渐意识到ACM的困难,我还是做不到把所有时间都梭哈在这上面。这种状态从暑假回来就一直持续着。今天趁着时间稍稍充裕,在AcWing上开始了动态规划的部分,其实暑假就在洛谷上写过了10多道动态规划了,今天做了两道题————01背包、完全背包,做个总结:

先贴优化后的核心代码

01背包

    for (int i=1;i<=n;i++){
            for (int j=V;j>=v[i];j--){
                    dp[j]=max (dp[j],dp[j-v[i]]+w[i]);
            }
        }

完全背包

for (int i=1;i<=n;i++){
	for (int j=v[i];j<=V;j++){
		dp[j]=max (dp[j],dp[j-v[i]]+w[i]);
	}
}

( dp[i][j] 表示从前 i 种物品中进行选择,总体积小于等于 j 所能获得的最大价值)

观察发现两段代码都有dp[j]=max (dp[j],dp[j-v[i]]+w[i])只是j的遍历顺序不同,要理解为什么遍历影响这么大要从动态规划的基本形式看————

01背包

for (int i = 1; i <= n; i ++ ){
    for (int j = 1; j <= m; j ++){
        if(v[i] <= j){
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
        }
        else{
            dp[i][j] = dp[i - 1][j];
        }
    }
}

因为 dp[i][j] 从 dp[i-1][j] 和 dp[i-1][j-v[i]]+w[i] 中得来,所以转化成滚动数组的时候 dp[j] 是依赖于上一层的,应该是还没有更新的,所以采用倒序;

完全背包

    for(int i = 1; i <= n; i ++ ){
        for(int j = 0; j <= m; j ++ ){
            if(v[i] <= j)
                dp[i][j] =max(dp[i - 1][j], dp[i][j - v[i]] + w[i]);
            else
                dp[i][j] = dp[i - 1][j];
        }
    }

可以发现,在更新 dp[j] 时,dp[j-v[i]] 必须是“本轮已经更新过的”。也就是说,我们必须让内层的j从小到大,这样 dp[j-v[i]] 在本轮i中已经更新过,包含了多次使用第i个物品的信息,这正是二维式 dp[i][j-v[i]] 的含义。

直觉记忆:

01背包:上一层转移,倒序

完全背包:本层转移,正序

最后想说,对于任何背包问题,应该先写出动态规划的基本形式,然后再进行优化。

01背包模版

完全背包模版

//希望这篇文章能再次激起我算法学习的动力吧,尽量每天对学习内容做个总结。

共勉!