01 背包

        不得不说 真的好折磨 

        关于01背包 我自己真正着手写的第一道题目不是经典的在规定重量里使价值最大的那个题目 而是洛谷里的采草药题目

 P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

        所以用time和value好像更顺手一点..

先放下二维数组的代码吧

#include<iostream>
#include<cstring>
int main()
{
    using namespace std;
    int T,M;
    int time[105];//时间
    int value[105];//价值
    int dp[105][1005];//背包 表示在面对第i件物品,且背包容量为j时所能获得的最大价值
    while(cin>>T>>M)//输入规定时间及草药数目
    {
        for(int i=1;i<=M;++i)//输入所有草药的时间及其价值
            cin>>time[i]>>value[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=M;++i)
            for(int j=0;j<=T;++j)//这里顺序逆序都可以
            {
                if(j<time[i])//如果当前的限定时间不足以采取当前的草药
                    dp[i][j]=dp[i-1][j];//就复制上一层的值
                else
                    dp[i][j]=max(value[i]+dp[i-1][j-time[i]],dp[i-1][j]);//将上一层的值(没有采当前的草药i)与当前草药价值加上剩余时间能够采取的最优草药价值(采了当前的草药)进行比较 取更优(即价值总和最大的那一个)
            }
        cout<<dp[M][T]<<endl;
    }
    return 0;
}

        ...ono放完代码也没什么想说的了 或者说是没什么我能说的了 感觉好像要带图才能讲明白

        不过 不会吧不会吧 这时候的yjy已经连二维数组都忘光了嘛 /敲打

        好叭 如果真的忘了的话可以去搜搜别人的博客吧 毕竟你也不会做图 别人的博客会更清晰一些

现在讲一维数组

        在01背包中空间复杂度可以进行优化 将二维数组改为一维数组 即减掉选哪个物品这一维 把时间这一维留下来 (所以dp[ j ]代表的是当时间限定为j的时候,能拿到的价值最大的东西)因为循环其实都只会用到上一层和当前层的数据 (我们知道二维数组中 行/层 代表物品 列代表时间 所以我们要舍去的是物品这一维)前面的不需要保存下来了

        但我们并不需要用两层来存储 只需要一层就好了 只需要逆序计算 因为是从后往前运算 当运用到上一层数据的时候 前面的数据还没有更新 就相当于用上一层数据了 下面会再讲 可以看完再回过头看这段话

(之前看了好多博客脑子一直糊里糊涂 不知道到底二维数组怎么能突然变成一维数组的 也不知道这里的一维数组它代表了啥含义 所以脑子一直混沌了一天 看了很多博客也理解不进去 感觉自己好像每次学东西都会突然这样 遇到一个点不明白就突然脑子就转不动了怎么都理解不下去 不知道该怎么办了)

        在二维数组中 j的循环顺序和逆序都可以 因为他用的都是上一层的数据 但改为一维数组时 j一定要逆序循环 因为一维数组的更改大多是覆盖 在计算dp[j]的时候 会用到dp[j-time[i]]的数据 这里的 dp[j-time[i]] 指的是还没有拿第i个物品时 第i-1层的状态(是类似于二维数组的第i-1层 一维数组里只有一层啦)如果顺序的话 在计算dp[j]时 dp[j-time[i]]的值已经更改过了 即dp[j-time[i]]的值已经是拿了第i个物品的状态了 这与01背包的原意不符(听说这里和完全背包有一定联系?但是还没有学到完全背包诶 等学完了再回来看看是哪里像)所以一定要逆序!

再最后理一下 这里也是主要借鉴紫书p273最后一段话

        dp数组的计算是从上到下、从右到左的 在计算dp( i , j )之前 dp[ j ]里保存的就是dp( i-1 , j )的值 而dp[ j - time[ i ] ]里保存的是dp( i-1 , j - time [ i ] ) 而不是dp( i , j - time [ i ] )——别忘了 j 是逆序枚举的,此时dp( i , j - time [ i ] )还没有算出来

        这样 dp[ j ]=max( dp[ j ],dp[ j - time[ i ] + value[ i ] )实际上是把max{ dp( i-1 , j ),dp( i-1 , j - time [ i ] )} 中,覆盖掉dp[j]原来的dp( i-1 , j )

        一维数组是二维数组在空间复杂度上的优化 但也有一定的不足之处 比如打印方案较为困难 在动态规划结束之后 只有一个阶段的状态值 而没有前面的值 虽然使用二维数组也不是很方便 

        这里一直讲的一维数组好像也叫做滚动数组 可能因为一直在滚动刷新值吧

        在滚动数组的弊病中 紫书还提到了字典序  (但是字典序是啥呢......)

下面是一维数组的代码:

for(int i=1;i<=M;++i)
    for(int j=T;j>=time[i];--j)//注意这里一定要逆序 并且只要循环到time[i]就好了 因为前面的数据不用再重抄一遍了 在一维数组里是覆盖 二维数组里有很多的复制 所以不用像二维数组一样每次都要把每一层填满
        dp[j]=max(dp[j],dp[j-time[i]]+value[i]);

学到这里了 可以用一维数组重新解一下这道最最最最经典的01背包问题 巩固一下

2. 01背包问题 - AcWing题库

我现在觉得我很可以了 让我再重新来看看之前个人赛没有写出来的题目吧

Problem - 2546 (dingbacode.com)

 ......

哦原来还是写不出来

题意:

        给出卡的余额m以及菜的数量n及分别对应的价格 只要自己卡里的钱大于等于5元就可以买任何菜品 问卡里的余额最少可以是多少

(讲讲对于这个题目自己最开始的想法 自己一直在想余额最少余额最少 甚至写代码的时候用的是min函数 想找余额最少的最优 啊啊不知道怎么说 总之后来越做越乱 不知道自己在干嘛)

分析:

        可以转换一下思维 要使卡里的余额最少 其实就是花的钱要尽可能的最多 最好的情况是能在余额还剩五块钱的时候买最贵的菜 现在我们只要让买其他菜剩下的钱尽可能大于且接近5元就好了

        那么我们就先留出五块钱买最贵的菜 也就是让m(卡的余额)先减5 再用m-5的钱对n-1个菜品(除了最贵的菜 剩下的其他菜)进行动态规划 寻找最优解(即m-5的钱能买到的最贵的菜品)这样想就又能回到最传统的01背包问题了

        最开始的时候要对所有菜品进行排序 我一开始一直执着于想用一个max变量存储最贵的菜品价格 但是这样后面的动态规划就不太容易进行了诶 

        最后再用最接近5元的钱买下最贵的菜品 得到的就是余额最小的情况

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
int main()
{
    using namespace std;
    int n,m,price[1005];
    while(cin>>n)//菜的数目
    {
        if(n==0)
            break;
        for(int i=1;i<=n;++i)
            cin>>price[i];//各种菜的价格
        sort(price+1,price+n+1);//对所有菜进行排序
        cin>>m;//卡里的余额
        if(m<5)
            cout<<m<<endl;//如果余额本身就小于5 就直接输出
        else
        {
            int dp[1005];
            memset(dp,0,sizeof(dp));
            m-=5;
            //用m-5的钱买最贵的东西
            for(int i=1;i<=n-1;++i)//遍历每一个菜
                for(int j=m;j>=price[i];--j)
                    dp[j]=max(dp[j],dp[j-price[i]]+price[i]);
            //用剩下的钱再买最贵的东西
            cout<<m-dp[m]-price[n]+5<<endl;
        }
    }
    return 0;
}

        ...突然感觉自己写的好乱 希望下次再回来看的时候自己能看懂吧

        关于01背包好像还有些优化及题型 但是我还没有看懂5555 感觉每次都是刚刚好像抓住了一点东西又突然消失 这种感觉真不舒服

        被迫停止01背包了 过段时间再来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值