题目描述(复制过来的)
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第ii件物品的价格为v[i]v[i],重要度为w[i]w[i],共选中了kk件物品,编号依次为j_1,j_2,...,j_kj1,j2,...,jk,则满意度为:v[j_1]*w[j_1]+v[j_2]*w[j_2]+ … +v[j_k]*w[j_k]v[j1]∗w[j1]+v[j2]∗w[j2]+…+v[jk]∗w[jk]。(其中 * 为乘号)
请你帮助王强计算可获得的最大的满意度。
输入描述:
输入的第 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 是所属主件的编号)
输出描述:
输出一个正整数,为张强可以获得的最大的满意度。
示例1
输入:
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
输出:
2200
复制
示例2
输入:
50 5 20 3 5 20 3 5 10 3 0 10 2 0 10 1 0
输出:
130
解题思路:
解这道题必须了解0-1背包问题(可以在b站搜索了解一下)。对于0-1背包问题,考虑物品i时,若背包容量足够加入物品i,只需要比较不加入物品i的价值与预留物品i容量后背包最大价值+物品i价值哪个更大。
f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])
其中f[i−1][j−w[i]]为预留物品i后的最大价值,v[i]为物品i价值
这道题在0-1背包问题的基础上加入了附件的概念,只有购买了主物件才能购买附件。以题目表述的示例1为例,2 3是附件,1 4 5是主件。附件不单独考虑,附件应该作为背包容量增加时,主件是否加入背包的子问题,借用一张图来说明问题:
当背包容量足够加入物品i和物品i的附件1,附件2时,考虑物品i(主件)是否加入背包的情况分为:
1. 不加入物品i :
f[i−1][j]
2. 预留物品i的空间,加入物品i:
f[i−1][j-weight[i]]+value[i]
3. 预留物品i与附件1(o1)的空间加入物品i和附件1:
f[i−1][j-weight[i]-weight[o1]]+value[i]+value[o1]
4. 预留物品i与附件2(o2)的空间加入物品i与附件2:
f[i−1][j-weight[i]-weight[o2]]+value[i]+value[o2]
5. 预留物品i与附件1,2的空间加入物品i与附件1,2
f[i−1][j-weight[i]-weight[o2]-weight[o1]]+value[i]+value[o2]+value[o1]
f[i][j]为以上五种情况的最大值(仅考虑背包容量足够加入物品i和物品i的附件1,附件2时),若背包容量只能放入物品i和附件1,只需要考虑情况1 2 3,比较这三种情况
代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Beibao {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
String[] strings = s.split(" ");
int money = Integer.parseInt(strings[0]);
int num = Integer.parseInt(strings[1]);
List<God> godList = new ArrayList<>();
while (in.hasNextInt()) { // 注意 while 处理多个 case
String s1 = in.nextLine();
String[] strings1 = s1.split(" ");
God god = new God(Integer.parseInt(strings1[0]),Integer.parseInt(strings1[1]),Integer.parseInt(strings1[2]));
godList.add(god);
}
for (int i = 0; i < godList.size(); i++) {
God god = godList.get(i);
if (!(god.getBelong()==0)){
List<God> godList1 = godList.get(god.getBelong() - 1).getGodList();
if (godList1!=null){
godList1.add(god);
}else {
godList1 = new ArrayList<>();
godList1.add(god);
godList.get(god.getBelong() - 1).setGodList(godList1);
}
}
}
int ll = godList.size();
for (int i = 0; i < ll; i++){
God god = godList.get(i);
if (god.getBelong()!=0){
godList.remove(i);
i--;
ll--;
}
}
int[][] dp = new int[godList.size()+1][money/10+1];
for (int i = 0; i <= godList.size(); i++) {
for (int j = 0; j <= money/10; j++) {
if (i==0){
dp[i][j] = 0;
}else {
God god = godList.get(i-1);
int godMoney = god.getP();
int godImp = god.getV();
int value = godMoney*godImp;
List<God> childList = god.getGodList();
int f1Money = 0;
int f1Imp = 0;
int f2Money = 0;
int f2Imp = 0;
if (childList!=null){
for (int k = 0; k < childList.size(); k++) {
if (k==0){
f1Money = childList.get(k).getP();
f1Imp = childList.get(k).getV();
}
if (k==1){
f2Money = childList.get(k).getP();
f2Imp = childList.get(k).getV();
}
}
}
int value1 = f1Imp*f1Money;
int value2 = f2Money*f2Imp;
if (j*10<godMoney){
dp[i][j] = dp[i-1][j];
continue;
}
if (f1Money>f2Money){
if (j*10<f2Money+godMoney){
// 只能选物品i或不选
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value);
}else if (j*10>=f2Money+godMoney&&j*10<f1Money+godMoney){
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f2Money/10]+value+value2);
// 只能选物品i+便宜附件附件
}else if (j*10>=godMoney+f1Money&& j*10<godMoney+f1Money+f2Money){
// 物品i 只能选一个附件
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f2Money/10]+value+value2,dp[i-1][j-godMoney/10-f1Money/10]+value+value1);
}else {
// 可以选
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f2Money/10]+value+value2,dp[i-1][j-godMoney/10-f1Money/10]+value+value1,dp[i-1][j-godMoney/10-f1Money/10-f2Money/10]+value+value1+value2);
}
}else {
if (j*10<f1Money+godMoney){
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-godMoney/10]+value);
}else if (j*10>=f1Money+godMoney&&j*10<f2Money+godMoney){
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f1Money/10]+value+value1);
}else if(j*10>=godMoney+f2Money&& j*10<godMoney+f1Money+f2Money){
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f2Money/10]+value+value2,dp[i-1][j-godMoney/10-f1Money/10]+value+value1);
}else {
dp[i][j] = myMax(dp[i-1][j],dp[i-1][j-godMoney/10]+value,dp[i-1][j-godMoney/10-f2Money/10]+value+value2,dp[i-1][j-godMoney/10-f1Money/10]+value+value1,dp[i-1][j-godMoney/10-f1Money/10-f2Money/10]+value+value1+value2);
}
}
}
}
}
System.out.println(dp[godList.size()][money/10]);
}
private static int myMax(int...a) {
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i:a
) {
arrayList.add(i);
}
Collections.sort(arrayList);
return arrayList.get(arrayList.size()-1);
}
}
class God{
private int p;
private int v;
private int belong;
private List<God> godList;
public God(int p, int v, int belong) {
this.p = p;
this.v = v;
this.belong = belong;
}
@Override
public String toString() {
return "God{" +
"p=" + p +
", v=" + v +
", belong=" + belong +
", godList=" + godList +
'}';
}
public God() {
}
public int getP() {
return p;
}
public void setP(int p) {
this.p = p;
}
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public int getBelong() {
return belong;
}
public void setBelong(int belong) {
this.belong = belong;
}
public List<God> getGodList() {
return godList;
}
public void setGodList(List<God> godList) {
this.godList = godList;
}
}
结果:
总结:
本题需要把物品i附件作为物品i是否加入背包的子问题,所以需要考虑的情况比较多,考虑清楚就能解决问题。