算法竞赛入门-01背包问题

前言

首先我们来了解一下什么是背包问题,其题目大意是对于一定容量的背包,给出一些物品,解出在容量内所能得到物品价值的最大值。背包问题其实是DP(动态规划)中的一类问题,学习背包问题可以让我们深刻的理解DP算法思想,无论是面试题、算法竞赛,DP都是至关重要的一类算法,今天我来为大家总结常见的01背包思路分析与问题分析。

01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能装入一次

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

算法思路分析

背包问题给人最大的疑惑就是,这件物品我是该选,还是不该选,存在必选的东西吗?对于这类选不选的问题常见的算法有三种:

  1. 贪心
  2. 暴力搜索
  3. DP
假算法:贪心

第一次接触到背包问题的人可能会往着贪心的方向去思考,但是很遗憾的是01背包问题并不能被贪心算法解决。对于贪心算法我们首先需要制定最优策略。对于这个问题给出三种可能的最优策略

  1. 价值优先
    从剩余的物品中,选出可以装入背包的价值最大的物品,价值最大的物品首先被装入,然后是下一个价值最大的物品,如此继续下去,直到装满背包为止。反例:考虑n=2, w=[100,10,10], v =[20,15,15], c = 105。当利用价值贪婪准则时,获得的解为x= [ 1 , 0 , 0 ],这种方案的总价值为20。而最优解为[ 0 , 1 , 1 ],其总价值为30。
  2. 体积优先
    从剩余的物品中,选择可装入背包的重量最小的物品。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑n= 2 ,w=[10,20], p=[5,100], c= 2 5。当利用重量贪婪策略时,获得的解为x =[1,0], 比最优解[ 0 , 1 ]要差
  3. 性价比(价值、体积)优先
    按比值的降序来排序,从第一项开始装背包,然后是第二项,依次类推,尽可能的多放,直到装满背包。利用此策略试解n= 3 ,w=[20,15,15], p=[40,25,25], c=30 时的最优解。虽然按性价比非递(增)减的次序装入物品不能保证得到最优解,但它是一个直觉上近似的解。
暴力搜索

暴力搜索理论上是可行的,DFS与BFS都可以达到我们想要的效果,对于每件物品我们将选与不选都记录下来,选取价值最大的即可。但是搜索的算法复杂度是O( 2 n 2^{n} 2n)当物品数量大于12时,我们将无法在1s内解决问题。

DP

有一句话叫DP是对搜索的优美优化,对于这句话我是十分赞同的,一般在遇到问题可以用搜索来解决,但是不满足时间条件时,我们往往可以尝试使用DP来优化。对于DP问题我们需要确定的最重要的问题是分析状态以及状态该如何转移

状态

首先考虑状态的维度,背包问题是在n个物品中选取满足体积之和小于v的物品的最大价值.将其转化为二维状态表示,f[n][v]=max;那么对于一般状态f[i][j] 就代表在前i件物品中体积为j的最大价值

状态转移

在面临第i件物品是否选择时,对于i>1,f[i-1][j] 的值已经确定且根据定义是最优解那么只剩下两种情况
1.首先如果不选该件物品那么答案和前一种相等f[i][j]=f[i-1][j];
2.如果选择该件物品,首先背包容量确定为 j 要选取该件物品状态将会由扣除该件物品的体积的状态转移即:f[i][j]=f[i-1][j-v[i]]+w[i]

将两者取max,就是f[i][j] 的最优解了。
算法复杂度O( n 2 n^{2} n2
空间复杂度O( n 2 n^{2} n2

#include<iostream>
using namespace std;
int f[1005][1005],v[1005],w[1005];// f[i][j], j体积下前i个物品的最大价值 
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j<v[i])f[i][j]=ans[i-1][j];//体积不够无法装下当前物品只能不选
            else 
            {
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);//负责取最大
            }
        }
    }
    cout<<f[n][m];
    return 0;
}

一维优化

上面我们已经成功的解决了01背包的问题,但是能否更加更加优化呢,我们发现当前的f[i][j] 只取决于上一轮的状态,这是否意味着我们可以只用一维数组来记录状态f[i-1][j] 呢?显然是可以的。
此时我们用f[j] 来表示之前的f[i-1][j]

for(int i = 1; i <= n; i++) //用外层循环来表示这是对于第几个物品的抉择
    for(int j = m; j >= 0; j--)//逆序
    {
        if(j < v[i]) 
            f[i][j] = f[i - 1][j];  // 优化前
            f[j] = f[j];            // 优化后,该行自动成立,可省略。
        else    
            f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);  // 优化前
            f[j] = max(f[j], f[j - v[i]] + w[i]);                   // 优化后
    }    


这里注意到我们将内层循环逆序了,这里要注意是必须需要逆序,对于二维版本内层循环的顺序是无所谓的,二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态,造成了污染

完整代码,可以看到更加的简洁
#include<iostream>
using namespace std;
int v[1010],w[1010];
int f[1010];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    for(int j=m;j>=v[i];j--)
    {  
        f[j]=max(f[j-v[i]]+w[i],f[j]);
    }
    cout<<f[m];
    
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
粒子群算法是一种用于在连续空间中搜索函数极值的优化算法,但也可以被应用于离散问题,比如0-1背包问题。在离散粒子群算法中,将离散问题空间映射到连续粒子运动空间,并使用适当的更新规则来求解,同时保留经典粒子群算法的速度和位置更新运算规则。 0-1背包问题是一种组合优化的NP完全问题。给定一组物品,每个物品有自己的重量和价值,在限定的总重量内,我们需要选择哪些物品放入背包中以使得物品的总重量不超过背包容量,并且总价值最大化。 离散粒子群算法可以用来求解0-1背包问题。首先,需要将问题转化为离散粒子群算法可处理的形式。具体来说,可以将每个物品视为粒子的一个维度,将物品的选中与否作为粒子在该维度上的取值。然后,根据0-1背包问题的约束条件,设计适当的更新规则来更新粒子的速度和位置。通过迭代更新,最终可以得到最优的解,即选取哪些物品放入背包中以使得总价值最大化的解。 因此,粒子群算法可以应用于求解0-1背包问题,通过将离散问题转化为连续空间中的优化问题,并使用适当的更新规则来求解最优解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [粒子群算法求解0-1背包问题](https://blog.csdn.net/qq_54169998/article/details/126687443)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [【背包问题】离散粒子群算法求解0-1背包问题【含Matlab源码 1342期】](https://blog.csdn.net/weixin_63266434/article/details/129400525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值