刷题笔记:动态规划01背包 -----购物单问题

动态规划01背包

例题

在这里插入图片描述

在这里插入图片描述

分析

本题是一个很明显的动态规划题目,如果使用枚举法来做的话,时间复杂度是指数级的,当数据量大的时候很容易超出时间限制。

根据题目要求来看,由于每个物品只能购买一次,所以很明显我们可以使用01背包的算法来做本题。

在做01背包的题目时,我们首先要弄清楚最重要的条件:
(1).物体有哪些属性?什么属性是要被装的?
(2).背包装什么?
(3).背包容量多大?

做背包时题目会给出N多个属性信息,我们需要在这些信息中找到正确的选项作为放入背包的选项。
从本题来看,我们一件物品有2个属性:
1.价格 2.满意度。

确定哪个是被装的属性可以从题目给的限制入手,题目有两个要求:
1.购买物品的总价格不能超过本金N;
2.购买物品的数量不能超过m;

由于每个物品只能被购买一次,购买物品的数量不能超过m,所以我们可以确定被装的物品属性就是物品的价格了,由此我们也可以确定背包的容量尺度为本金N。

于是一个简单的01背包模型就可建立起来了
在这里插入图片描述
对照经典的背包模型:
砖块重量 -----物品价格
砖块价值------物品满意度
重量背包------价格背包

完成了背包模型建立,本题还有一个难点:主件和附件问题。

当我们购买附件时,我们必须购买主件,所以当我们添加附件放入背包的时候,附件的价格为(附件价格+主件价格),例如:
当我们购买灯泡的时候,灯泡价格是5元,灯泡是台灯的附件,台灯价格为25元,购买灯泡之前必须购买台灯,所以我的本金最少要减去30元。

我们可以将灯泡的价格与台灯的价格合并在一起,作为一个新物体来看待,但是由于一个主件可能有多个附件,所以如果如此操作的话会忽略N个附件和主件一起购买的情况,例如:
主件为:电脑(8000元)
附件为:打印机(800元)、复印机(600元)
若按顺序遍历,遍历到打印机和复印机时,仅仅将打印机的价格改为(8000+800)元、打印机价格改成(8000+600),我们的确能判断只买电脑、只买电脑和打印机、只买电脑和复印机这三种情况,还缺少购买电脑、打印机、复印机(8000+800+600)这中情况。

所以我们附件不应该单独遍历,而应该将主件与附件组合起来,只有遍历到主件的时候才判断情况。

当我们做01背包的时候,我们判断是否将物品A的放入背包时,要判断将其他物品拿出放入A的价值大还是不放入A保持原状的价值更大,来看是否要将物品A放入,例:

背包 dp[100] ={0};
物品 A 重量 70 ,价值 70
物品 B 重量 40 ,价值 65

在放入A之前,背包以及放入了一个物体B,则背包中最大价值为65,占用了40的空间,所以我们可以将dp[40]~dp[100]赋值为65;(注意:此处dp[i] = K为,当背包只有 i 这么大时能装入的最大价值K

判断我们是否将A放入背包时,需要将背包空间腾出来,需要腾出70的空间,由于背包容量为100,所以如果放入A的话,背包内物品的总价值为dp[30] + 70 = 0 + 70;

如果不放入A的话,背包容量为70时所放物品最大价值为dp[70] = 65,小于70,所以要拿出B,放入A将矩阵更新为dp[70]~dp[100] = 70。

代码

代入本题时,我们只需要多判断加入A的时候是否加入附件的情况。

首先我们先将主件与附件合并,由于题目提示每个主件最多只有1~2个附件,所以我们只需要在主件后面预留两个空间用来存放附件信息:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int x;
cin>>x;
vector<vector<int>> data(x+1,vector<int>(6,0));//最多两个附件,所以申请6的空间大小
while(x--)
    {
        int val,w,q;
        cin >> val>>w>>q; 
        if(q > 0)
        {
            if(data[q][2] == 0)
            {
                data[q][2] = val/10;
                data[q][3] = w;
            }
            else 
            {
                data[q][4] = val/10;
                data[q][5] = w;
            }
        }
        else if(q == 0)
        {
            data[k][0] = val/10;
            data[k][1] = w;
        }
        k++;
    }

接下来就是常规01背包的问题了,data[i][0]、data[i][2] 、data[i][4]分别为主件价格、附件1价格、附件2价格,data[i][1]、data[i][2] 、data[i][3]分别为主件重要程度、附件1重要程度、附件2重要程度。
代码就不多解释了,整体代码如下:

#include<bits/stdc++.h>

using namespace std;

int main()
{
    int n,x;
    cin>>n;
    cin>>x;
    vector<vector<int>> data(x+1,vector<int>(6,0));
    vector<int> dp(n/10+1,0);
    int k = 1;
    while(x--)
    {
        int val,w,q;
        cin >> val>>w>>q; 
        if(q > 0)
        {
            if(data[q][2] == 0)
            {
                data[q][2] = val/10;
                data[q][3] = w;
            }
            else 
            {
                data[q][4] = val/10;
                data[q][5] = w;
            }
        }
        else if(q == 0)
        {
            data[k][0] = val/10;
            data[k][1] = w;
        }
        k++;
    }
    for(int i = 0;i < data.size();i++)
    {
        for(int j = dp.size()-1;j >= data[i][0];j--)//一定要从后向前遍历,不然会有物品被重复放入
        {
            dp[j] = max(dp[j],dp[j-data[i][0]] + data[i][0]*data[i][1]);
            if( j >= data[i][0] + data[i][2]) 
                dp[j] = max(dp[j],dp[j-(data[i][0] + data[i][2])] 
                            + data[i][0]*data[i][1]
                            + data[i][2]*data[i][3]);
             if( j >= data[i][0] + data[i][4]) 
                dp[j] = max(dp[j],dp[j-(data[i][0] + data[i][4])] 
                            + data[i][0]*data[i][1]
                            + data[i][4]*data[i][5]);
            if( j >= data[i][0] + data[i][2] + data[i][4]) 
                dp[j] = max(dp[j],dp[j-(data[i][0] + data[i][2]+ data[i][4])] 
                            + data[i][0]*data[i][1]
                            + data[i][2]*data[i][3]
                            + data[i][4]*data[i][5]);
        }
    }
    cout << dp[n/10]*10;
    
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
买书问题 dp实现 题目:买书 有一书店引进了一套书,共有3卷,每卷书定价是60元,书店为了搞促销,推出一个活动,活动如下: 如果单独购买其中一卷,那么可以打9.5折。 如果同时购买两卷不同的,那么可以打9折。 如果同时购买三卷不同的,那么可以打8.5折。 如果小明希望购买第1卷x本,第2卷y本,第3卷z本,那么至少需要多少钱呢?(x、y、z为三个已知整数)。 1、过程为一次一次的购买,每一次购买也许只买一本(这有三种方案),或者买两本(这也有三种方案), 或者三本一起买(这有一种方案),最后直到买完所有需要的书。 2、最后一步我必然会在7种购买方案中选择一种,因此我要在7种购买方案中选择一个最佳情况。 3、子问题是,我选择了某个方案后,如何使得购买剩余的书能用最少的钱?并且这个选择不会使得剩余的书为负数 。母问题和子问题都是给定三卷书的购买量,求最少需要用的钱,所以有"子问题重叠",问题中三个购买量设置为参数, 分别为i、j、k。 4、的确符合。 5、边界是一次购买就可以买完所有的书,处理方式请读者自己考虑。 6、每次选择最多有7种方案,并且不会同时实施其中多种,因此方案的选择互不影响,所以有"子问题独立"。 7、我可以用minMoney[i][j][k]来保存购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱。 8、共有x * y * z个问题,每个问题面对7种选择,时间为:O( x * y * z * 7) = O( x * y* z )。 9、用函数MinMoney(i,j,k)来表示购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱,那么有: MinMoney(i,j,k)=min(s1,s2,s3,s4,s5,s6,s7),其中s1,s2,s3,s4,s5,s6,s7分别为对应的7种方案使用的最少金钱: s1 = 60 * 0.95 + MinMoney(i-1,j,k) s2 = 60 * 0.95 + MinMoney(i,j-1,k) s3 = 60 * 0.95 + MinMoney(i,j,k-1) s4 = (60 + 60) * 0.9 + MinMoney(i-1,j-1,k) s5 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s6 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s7 = (60 + 60 + 60) * 0.85 + MinMoney(i-1,j-1,k-1)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值