数据结构与算法-购物单

题目描述

王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。

每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。

王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。

满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第 \(i\) 件物品的价格为 \(v[i]\) ,重要度为 \(w[i]\) ,共选中了 \(k\) 件物品,编号依次为 \(j_1,j_2,...,j_k\) ,则满意度为:\(v[j_1]*w[j_1]+v[j_2]*w[j_2]+ … +v[j_k]*w[j_k]\)。(其中 * 为乘号)请你帮助王强计算可获得的最大的满意度。

输入描述:

输入的第 1 行,为两个正整数 N,m,用一个空格隔开:

(其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。)

从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q

(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:
输出一个正整数,为张强可以获得的最大的满意度。

示例

输入:
50 5
20 3 5
20 3 5
10 3 0
10 2 0
10 1 0

输出:
130

说明:
由第1行可知总钱数N为50以及希望购买的物品个数m为5;
第2和第3行的q为5,说明它们都是编号为5的物品的附件;
第4~6行的q都为0,说明它们都是主件,它们的编号依次为3~5;
所以物品的价格与重要度乘积的总和的最大值为10*1+20*3+20*3=130

问题分析

购物车本质上还是 0-1背包问题,只不过多了主件和附件。假设先不看附件,那么就和 0-1背包一样了。附件不能单独出现,要依赖于主件。

对应于背包问题,主件的个数就是物品的个数,考虑每个主件时要考虑可能出现的情况。

考虑每个物品时要考虑每种可能出现的情况,

  1. 主件,
  2. 主件+附件1,
  3. 主件+附件2,
  4. 主件+附件1+附件2,

在以上四种情况中找到最大值就能回归到0-1背包问题。所以我们先考虑一下普通的0-1背包问题。

对于一个可承重 \(C\) 的背包,我们假设所有物品的重量数据保存在 \(w[]\),所有价值数据保存在 \(v[]\)。那么我们有以下的推导式:

\[dp[i][j]=max(dp[i−1][j],dp[i−1][j−w[j]]+v[j]) \]

那么对于“购物单”这道题,我们可以有如下抽象:

\[dp[i][j] = max(dp[i-1][j],\left\{ \text{四种情况产生的价值} \right\} ) \]

代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){
    int N, m; // N 奖金 m 物品个数
    cin >> N >> m;
    N /= 10; // 由于所有的价格都是10的整倍数,所以可以均除10以简化运算复杂度

    int price, priority, hasAttachments;
    // 使用一个(m+1)X6的数组存储数据,m+1是根据物品编号,0作废;6考虑可能有附件的最多的情况
    vector<vector<int>> data(m+1, vector<int>(6, 0));

    for(int i = 1; i <= m; i++){
        cin >> price >> priority >> hasAttachments;
        // 主件
        if(hasAttachments == 0){
            data[i][0] = price/10;
            data[i][1] = priority;
        }
        // 第一个附件
        else if(data[hasAttachments][2] == 0){
            data[hasAttachments][2] = price/10;
            data[hasAttachments][3] = priority;
        }
        // 第二个附件
        else {
            data[hasAttachments][4] = price/10;
            data[hasAttachments][5] = priority;
        }
    }

    vector<vector<int>> dp(m+1, vector<int>(N+1, 0));
    for(int i = 1; i < m+1; i++){
        for(int j = 1; j < N+1; j++){
            int pricePrime = data[i][0];
            int priceAtta1 = data[i][2];
            int priceAtta2 = data[i][4];
            
            int priorPrime = data[i][1];
            int priorAtta1 = data[i][3];
            int priorAtta2 = data[i][5];

            dp[i][j] = j >= pricePrime ? max(dp[i-1][j - pricePrime] 
                                            + priorPrime * pricePrime, 
                                            dp[i-1][j]) : dp[i-1][j];
            dp[i][j] = j >= (pricePrime + priceAtta1) ? max(dp[i-1][j - pricePrime - priceAtta1]
                                                        + priorPrime * pricePrime 
                                                        + priorAtta1 * priceAtta1, 
                                                        dp[i][j]) : dp[i][j];
            dp[i][j] = j >= (pricePrime + priceAtta2) ? max(dp[i-1][j - pricePrime - priceAtta2]
                                                        + priorPrime * pricePrime 
                                                        + priorAtta2 * priceAtta2, 
                                                        dp[i][j]) : dp[i][j];
            dp[i][j] = j >= (pricePrime + priceAtta1 + priceAtta2) ? 
                                                        max(dp[i-1][j - pricePrime - priceAtta1 - priceAtta2]
                                                        + priorPrime * pricePrime 
                                                        + priorAtta1 * priceAtta1
                                                        + priorAtta2 * priceAtta2, 
                                                        dp[i][j]) : dp[i][j];
        }
    }
    cout << dp[m][N] * 10 <<endl;
    return 0;
}

转化为01背包问题

在解决完全背包问题时,我们考虑了每个物品可能出现的次数。在解决购物车问题时,每个物品时要考虑每种可能出现的情况,

  1. 主件,
  2. 主件+附件1,
  3. 主件+附件2,
  4. 主件+附件1+附件2,

我们将存在的情况当作新的物品,这样购物车问题就可以当作 01 背包问题。

posted @ 2022-08-09 15:06  Logan_Xu  阅读(295)  评论(0编辑  收藏  举报