avatar
fireworks99
keep hungry keep foolish

HJ16 购物单

题目描述

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

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

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

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

满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第i件物品的价格为v[i],重要度为w[i],共选中了k件物品,编号依次为j1,j2,…,jk,则满意度为:v[j1] x w[j1] + v[j2] x w[j2] + … + v[jk] x w[jk]。

请你帮助王强计算可获得的最大的满意度。

示例

输入:

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

输出:

2200

思路

如果没有主件附件的概念,那么基于“每件物品只能购买一次”这一点,此问题是一个裸的01背包问题。(只不过01背包模板问题里价值直接给出,这里给出的是重要程度,用它乘上价格才是价值。相当于在全裸的基础上穿了双袜子的程度

Code(fake)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

int n, m;
int v[65], p[65], q[65];
int ans[32005];

void solve() {
    memset(ans, 0, sizeof(ans));
    for(int i = 1; i <= m; ++i) {
        for(int j = n; j >= v[i]; --j) {
            ans[j] = max(ans[j], ans[ j - v[i] ] + v[i] * p[i]);
        }
    }
    cout << ans[n] << '\n';
    return ;
}

int main() {
    while (cin >> n >> m)
    {
        memset(v, 0, sizeof(v));
        memset(p, 0, sizeof(p));
        memset(q, 0, sizeof(q));
        for(int i = 1; i <= m; ++i) {
            scanf("%d%d%d", &v[i], &p[i], &q[i]);
        }
        solve();
    }
    return 0;
}

Code(Real)

这题卡了我一天……我一直把附件跟主件平等看待,认为他们是并列关系,秉着这一理念越做越乱,越做越复杂。

后来看了题解,题目描述里有这样一句话:“每个主件可以有 0 个、 1 个或 2 个附件”,也就是说每个主件的附件是有限的,最多俩,因此可以把附件看做主件的一部分,在考虑第 i 个主件是否放入背包时,有以下几种方案:

  1. 不放入主件
  2. 只放入主件
  3. 放入主件 + 附件1(若有)
  4. 放入主件 + 附件2(若有)
  5. 放入主件 + 附件1 (若有)+ 附件2(若有)

只遍历主件,从上述5种情况中取最大值即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int n, m;
int v[65], p[65], q[65];
int f1[65], f2[65];
int ans[32005];

/*
a: 不放该主件
b: 只放入该主件
c: 放入主件 + 附件1
d: 放入主件 + 附件2
e: 放入主件 + 附件1 + 附件2
*/

void solve() {
    memset(ans, 0, sizeof(ans));
    for(int i = 1; i <= m; ++i) {
        //只遍历主件,把附件看做主件的一部分
        //从字面上也看得出来是主从关系,不是平等并列关系
        //所以我一开始的思路(平等看待主件与附件)就是错的
        if(q[i] == 0) {
            for(int j = n; j >= v[i]; --j) {
                int a = ans[j];
                int b = ans[ j - v[i] ] + v[i] * p[i];
                int c = 0, d = 0, e = 0;
                if(f1[i] && j >= (v[i] + v[ f1[i] ]))
                    c = ans[ j - v[i] - v[ f1[i] ] ] + v[i] * p[i] + v[ f1[i] ] * p[ f1[i] ];
                if(f2[i] && j >= (v[i] + v[ f2[i] ]))
                    d = ans[ j - v[i] - v[ f2[i] ] ] + v[i] * p[i] + v[ f2[i] ] * p[ f2[i] ];
                if(f1[i] && f2[i] && j >= (v[i] + v[ f1[i] ] + v[ f2[i] ]))
                    e = ans[ j - v[i] - v[ f1[i] ] - v[ f2[i] ] ] 
                        + v[i] * p[i] + v[ f1[i] ] * p[ f1[i] ] + v[ f2[i] ] * p[ f2[i] ];
                ans[j] = max(a, max(b, max(c, max(d, e))));
            }
        }
    }
    cout << ans[n] << '\n';
    return ;
}

int main() {
    freopen("00input.txt", "r", stdin);

    while (cin >> n >> m)
    {
        memset(v, 0, sizeof(v));
        memset(p, 0, sizeof(p));
        memset(q, 0, sizeof(q));
        memset(f1, 0, sizeof(f1));
        memset(f2, 0, sizeof(f2));
        for(int i = 1; i <= m; ++i) {
            scanf("%d%d%d", &v[i], &p[i], &q[i]);
            if(q[i] > 0) {
                int to = q[i];
                if(f1[to] > 0) f2[to] = i;
                else f1[to] = i;
            }
        }
        solve();
    }
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy