King Bin's Budget Scheme

Luogu P1064

Problem Statement

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 $n$ 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

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

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 $0$ 个、$1$ 个或 $2$ 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 $n$ 元。于是,他把每件物品规定了一个重要度,分为 $5$ 等:用整数 $1 \sim 5$ 表示,第 $5$ 等最重要。他还从因特网上查到了每件物品的价格(都是 $10$ 元的整数倍)。他希望在不超过 $n$ 元的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 $j$ 件物品的价格为 $v_j$,重要度为 $w_j$,共选中了 $k$ 件物品,编号依次为 $j_1,j_2,\dots,j_k$,则所求的总和为:

$$v_{j_1} \times w_{j_1}+v_{j_2} \times w_{j_2}+ \dots +v_{j_k} \times w_{j_k}$$

请你帮助金明设计一个满足要求的购物单。

Input

第一行有两个整数,分别表示总钱数 $n$ 和希望购买的物品个数 $m$。

第 $2$ 到第 $(m + 1)$ 行,每行三个整数,第 $(i + 1)$ 行的整数 $v_i$,$p_i$,$q_i$ 分别表示第 $i$ 件物品的价格、重要度以及它对应的的主件。如果 $q_i=0$,表示该物品本身是主件。

Output

输出一行一个整数表示答案。

Sample Input

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

Sample Output

1
2200

Constraints

对于全部的测试点,保证 $1 \leq n \leq 3.2 \times 10^4$,$1 \leq m \leq 60$,$0 \leq v_i \leq 10^4$,$1 \leq p_i \leq 5$,$0 \leq q_i \leq m$,答案不超过 $2 \times 10^5$。

Solving

有依赖的背包模板题

有依赖的背包,意为部分物品的选取依赖于某个特定种类的物品,且各个种类间的物品选择关系互斥。此时则变成了特殊的01背包,即有依赖的背包,此时我们讨论的关键不再是要或不要,而是 不要 或 怎么要

所以做题整体的思路为,读入数据,标记每个被依赖的数据,记录依赖当前物品的个数,同时也记录那些物品依赖当前被依赖的物品,随后进行01背包操作:

每遍历到一个被依赖的物品,围绕此物品展开,获得所有依赖此物品的物品,再在其中作选择(不要或怎么要)。

以下是不进行一维空间压缩的程序代码,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//有依赖的背包,环绕不要和怎么要的问题。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define INF 0x3f3f3f3f
#define NINF -0x3f3f3f3f
#define Single
using namespace std;
using ll = long long;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int N = 32010;
const int M = 62;
bitset<M> is_major;
int followers[M][2];
int followcnt[M];
int cost[M];
int val[M];
void solve()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        int v,p,q;
        cin >> v >> p >> q;
        if(q==0) is_major[i] = true;
        else
        {
            followers[q][followcnt[q]] = i;
            followcnt[q]++;
        }
        cost[i] = v;
        val[i] = v * p;
    }
    int dp[m+1][n+1];
    memset(dp,0,sizeof dp);
    int ptr = 0;//由于不是每个物品都讨论拿或不拿所以需要一个指针,指向上一个需要展开讨论的主要物品,即二维dp表内的上一行. 
    for(int i=1,fan1,fan2;i<=m;i++)
    {
        if(is_major[i])
        {
            for(int j=0;j<=n;j++)
            {   
                //要法1 主附都不要 直接复制上一个主物品下的数值
                dp[i][j] = dp[ptr][j];
                //要法2 选择要不要主
                if(j >= cost[i])
                {
                    dp[i][j] = max(dp[i][j],dp[ptr][j-cost[i]]+val[i]);
                }
                //两个附件,没有则初始化为-1
                fan1 = followcnt[i] >= 1 ? followers[i][0] : -1;
                fan2 = followcnt[i] >= 2 ? followers[i][1] : -1;
                //要法3 选择主的情况下 选择附1
                if(j >= cost[i] + cost[fan1]&&fan1 != -1)
                {
                    dp[i][j] = max(dp[i][j],dp[ptr][j-cost[i]-cost[fan1]]+val[i]+val[fan1]);
                }
                //要法4 选择主的情况下 选择附2
                if(j >= cost[i] + cost[fan2]&&fan2 != -1)
                {
                    dp[i][j] = max(dp[i][j],dp[ptr][j-cost[i]-cost[fan2]]+val[i]+val[fan2]);
                }
                //要法4 选择主的情况下 两个附件都要
                if(fan1 != -1 && fan2 != -1 && j - cost[i] - cost[fan1] - cost[fan2] >= 0)
                {
                    dp[i][j] = max(dp[i][j],dp[ptr][j-cost[i]-cost[fan1]-cost[fan2]]+val[i]+val[fan1]+val[fan2]);
                }
            }   
            ptr = i ;//更新指针指向当前行
        }
    }
    cout << dp[ptr][n];
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int T;
#ifdef Single
    T = 1;
#else
    cin >> T;
#endif
    while(T--)
    {
        solve();
    }
    return 0;
}

一下是进行过空间优化后的一维DP程序片段,由于dp表中自动继承上一行的数值,所以不再需要指针ptr:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int dp[n+1];
    memset(dp,0,sizeof dp);
    for(int i=1;i<=m;i++)
    {
        if(is_major[i])
        {
            for(int j=n;j>=cost[i];j--)
            {
                dp[j] = max(dp[j],dp[j-cost[i]]+val[i]);
                int fan1 = followcnt[i] >= 1 ? followers[i][0] : -1;
                int fan2 = followcnt[i] >= 2 ? followers[i][1] : -1;
                if(j>=cost[i] + cost[fan1]&&fan1 != -1)
                {
                    dp[j] = max(dp[j],dp[j-cost[i]-cost[fan1]]+val[i]+val[fan1]);
                }
                if(j>=cost[i]+cost[fan2]&&fan2 != -1)
                {
                    dp[j] = max(dp[j],dp[j-cost[i]-cost[fan2]]+val[fan2]+val[i]);
                }
                if(j>=cost[i]+cost[fan1]+cost[fan2]&&fan1 != -1 && fan2 != -1)
                {
                    dp[j] = max(dp[j],dp[j-cost[i]-cost[fan1]-cost[fan2]]+val[i]+val[fan1]+val[fan2]);
                }
            }
        }
    }
    cout << dp[n];
Licensed under CC BY-NC-SA 4.0
Member of the Qilu University Of Technology ACM-ICPC Association
Built with Hugo
Theme Stack designed by Jimmy