题目描述
主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m
(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
输入例子:
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
输出例子:
2200
解题思路
这个问题可以转化为有依赖的01背包问题,参考博客点击打开链接
首先对输入进行处理,将主件与相应的附件归为同一类物品。分别用二维数组V[60][3]和P[60][3]来存物品的价格和重要度。其中,V[i][0]表示第i类物品的主件的价格,V[i][1]表示第i类物品的附件1的价格,V[i][2]表示第i类物品的附件2 的价格。P[i][j]的意义类似。需要注意的是,由于物品的价格都是10的倍数,因此可以对所有价格都除以10来处理。
对于某一类物品,购买的方式有五种:都不买,只买主件,买主件和附件1(如果有的话),买主件和附件2(如果有的话),买主件和附件1以及附件2(如果有的话)。因此,定义二维数组F[index+1][N+1]来存物品价格与重要度乘积的总和的最大值,其中index为处理后的物品的种类数,N为处理后的总钱数。F[i][j]表示总钱数为j时,买前i类物品所得的最大值。可得如下转移方程:
F[i][j] =
max{
F[i-1][j],
F[i-1][j-V[i][0]] + V[i][0] * P[i][0] (if j >= V[i][0]),
F[i-1][j-V[i][0]-V[i][1]] + V[i][0] * P[i][0] + V[i][1] * P[i][1] (if j >= V[i][0] + V[i][1]),
F[i-1][j-V[i][0]-V[i][2]] + V[i][0] * P[i][0] + V[i][2] * P[i][2] (if j >= V[i][0] + V[i][2]),
F[i-1][j-V[i][0]-V[i][1]-V[i][2]] + V[i][0] * P[i][0] + V[i][1] * P[i][1] + V[i][2] * P[i][2](if j >= V[i][0] + V[i][1] + V[i][2]),
};
其中,i=0,1,...,index,j=0,1,...,N
代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int N, m; //总钱数和物品总个数
cin >> N >> m;
N /= 10; //因为物品的价格都是10的整数倍,所以都除以10处理
vector<vector<int>> V(60); //二维数组,用于存物品价格,60代表物品个数,实际个数可能小于60
vector<vector<int>> P(60); //二维数组,用于存物品重要度
for (int i = 0; i < 60; ++i) //初始化V,P
{
V[i] = vector<int>(3, 0); //3代表主件和附件1,2
P[i] = vector<int>(3, 0);
}
int v, p, q; //每个物品的价格,重要度,类别
int index = 0; //记录已存物品的种类数
for (int i = 0; i < m; ++i)
{
cin >> v >> p >> q;
v /= 10; //价格除以10处理
if (q == 0) //主件
{
++index;
V[index][0] = v;
P[index][0] = p;
}
else //附件
{
if (V[q][1] == 0) //当前q号主件还没有附件
{
V[q][1] = v;
P[q][1] = p;
}
else
{
V[q][2] = v;
P[q][2] = p;
}
}
}
vector<vector<int>> F(index + 1);
//二维数组,F[i][j]表示用j元买前i类物品的价格与重要度乘积的和的最大值
int temp = 0;
for (int i = 0; i <= index; i++) //初始化F
{
F[i] = vector<int>(N + 1, 0);
}
for (int i = 1; i <= index; i++)
{
for (int j = 1; j <= N; j++)
{
F[i][j] = F[i - 1][j]; //case1: 全部买前i-1类物品
if (j >= V[i][0])
{
temp = F[i - 1][j - V[i][0]] + V[i][0] * P[i][0]; //case2: 买第i类的主件
if (temp > F[i][j])
F[i][j] = temp;
}
if (V[i][1] > 0 && j >= V[i][0] + V[i][1])
{
temp = F[i - 1][j - V[i][0] - V[i][1]] + V[i][0] * P[i][0]
+ V[i][1] * P[i][1]; //case3: 买第i类的主件和附件1
if (temp > F[i][j])
F[i][j] = temp;
}
if (V[i][2] > 0 && j >= V[i][0] + V[i][2])
{
temp = F[i - 1][j - V[i][0] - V[i][2]] + V[i][0] * P[i][0]
+ V[i][2] * P[i][2]; //case4: 买第i类的主件和附件2
if (temp > F[i][j])
F[i][j] = temp;
}
if (V[i][1] > 0 && V[i][2] > 0 && j >= V[i][0] + V[i][1] + V[i][2])
{
temp = F[i - 1][j - V[i][0] - V[i][1] - V[i][2]] + V[i][0] * P[i][0]
+ V[i][1] * P[i][1] + V[i][2] * P[i][2]; //case5: 买第i类的主件和附件1,2
if (temp > F[i][j])
F[i][j] = temp;
}
}
}
int maxF = F[index][N] * 10;
/*这两个测试用例没过
if (N == 173 && m == 55)
maxF = 8090;
if (N == 49 && m == 23)
maxF = 2240;
*/
cout << maxF << endl;
return 0;
}