01背包

这两天的upc被虐的很惨,动态规划,搜索都没接触过,现在学数据结构,没办法了,双管齐下吧,Orz!

01背包:

有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vivi,价值是 wiwi。

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

输入格式

第一行两个整数,NVN,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V10000<N,V≤1000
0<vi,wi10000<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8
01背包
(划分依据:依据最后一步的不同来)
令f[i][j]是前i个物品,容量不超过j的最大价值
最后一步也就是看看第i个物品的决策:
不选:那第i个物品及其之前的价值相同,所以f[i-1][j]
选:这里只包含了选,所以第i个物品及其之前的价值=f[i-1][j-vi]+w
*********************************我来debug,下面的说法有点问题QAQ,可以看上面的
01背包问题是最基础的动态规划
这个题属于有限集合求最大值的类型,一共有2^n的情况,可以采用闫式分析法
集合:所有只考虑前i个物品,且总体积不超过j的选出的集合
属性:集合中每一个方案的最大价值
确定状态转移方程一定要多做题!!
二维f[i][j]中i表示前i个物品,而j一般代表着限制条件,比如容量、重量等等
01分别代表着选与不选,二维01问题f[i][j]代表着前i个物品,背包容量j的情况下的最优解(记住是最优解),所以每一次i的增长,会随着价值和体积的变化最优解而不断变化
这个就很灵活,比起之前做的题方法死板板的,跳跃式的基本无缘的情况下,这种很方便了
当前的状态要依赖于上一个状态,上一个状态又要依赖于再上一个状态...有点点像递推啊有木有有木有
所以要定一个初始状态f[0][0]=0,前0个体积为0的情况下价值自然就是0啦
好,下面开始上重中之重,也就是核心部分:
刚刚都说了01背包中01代表着选与不选的问题,这下自然分两类:
①背包容量不够(此时的j<v[i])
这个情况下自然不选,这个不选的最优解其实就等于上一个物品的最优解(因为压根没选上啊,没进背包何谈更新最优解)
f[i][j]=f[i-1][j];
②背包容量足够的情况下,这个就看自主分配了
(1)不选,同理容量不够,大同小异:
f[i][j]=f[i-1][j];
(2)选:
刚刚说了此时的状态依赖于上一个状态
f[i][j]=f[i-1][j-v[i]]+w[i](上一个状态也就是前i-1个物品体积自然就是j-v[i](已经选上第i个了嘛),两边要等价,再加上选上的这个第i个物品的价值w[i])
然后判断一下最优解的情况,也就是求其最大值:
复制代码
#include<iostream>
using namespace std;
int f[1010][1010],v[1010],w[1010];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    f[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j<v[i])//装不下
                f[i][j]=f[i-1][j];
            else//装得下 
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
} 
复制代码

二维的f[i][j]本质上是可以求得任意合法的i与j的最优解

可是题目里只让求n个物品的最优解,那之前求的那些i不是浪费了么

所以此代码可以优化到一维,二维表示也就是f[n][j]

状态f[j]定义:N件物品背包容量j下的最优解

但为什么是逆序,

*************************************************************************************************************************************************

(3)为什么一维情况下枚举背包容量需要逆序?在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。

(4)例如,一维状态第i轮对体积为 33 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。

(5)简单来说,一维情况正序更新状态f[j]需要用到前面计算的状态已经被「污染」,逆序则不会有这样的问题。

作者:深蓝
链接:https://www.acwing.com/solution/content/1374/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

****************************************************************************************************************************************************

上面这是一位大佬写的,我自己懂的不是很彻底,就就直接复制粘贴过来了,留着自己思考QAQ

啊,二刷明白了,一维的优化其实是在二维的基础上进行优化,两个式子等价:

所以需要逆序就是从大到小循环f[j-v[i]]还没有更新过,这里面存的值就是上一层的值也就是二维的f[i-1][j-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]=f[i-1][j];//优化前
            if(j<v[i])
                f[j]=f[j]//优化后,0没有选上,背包容量没有任何的变化,也就是最优解没有啥变化 
                         //当然此步骤可以省略 
            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]); //优化后 
        }
    }
复制代码

综上再进行一步优化:只有当枚举的背包容量 >= v[i] 时才会更新状态

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

*********************************************************************************

关于状态f[j]的补充说明
二维下的状态定义f[i][j]是前 ii 件物品,背包容量 jj 下的最大价值。一维下,少了前 ii 件物品这个维度,我们的代码中决策到第 ii 件物品(循环到第i轮),f[j]就是前i轮已经决策的物品且背包容量 jj 下的最大价值

因此当执行完循环结构后,由于已经决策了所有物品,f[j]就是所有物品背包容量 jj 下的最大价值。即一维f[j]等价于二维f[n][j]。

同样复制刚刚那位大佬的

复制代码
#include<iostream>
using namespace std;
int f[1010],v[1010],w[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],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}
复制代码

最然最后优化的版本,连同输入一起进行

复制代码
#include<iostream>
using namespace std;
int f[1010],v,w;
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>v>>w;
        for(int j=m;j>=v;j--)
            f[j]=max(f[j],f[j-v]+w);
    }
    cout<<f[m]<<endl;
    return 0;
}
复制代码

 

posted @   小志61314  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示