主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
输入的第 1 行,为两个正整数N,m,用一个空格隔开。其中 N 表示总钱数, m 为可购买的物品的个数。
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v、p、q。
其中 v 表示该物品的价格,p 表示该物品的重要度,q 表示该物品是主件还是附件。
如果 q=0,表示该物品为主件,如果q>0,表示该物品为附件, q 是所属主件的编号。
数据范围:N<32000,m<60,v<10000,1<=p<=5。
输出一个正整数,为张强可以获得的最大的满意度。
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
2200
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
import java.util.ArrayList; import java.util.Scanner; /** * Description: * Author: fy * Date: 2024/11/14 7:21 */ public class Main { static class Products { int price; int important; int index; public Products(int price, int important, int index) { this.price = price; this.important = important; this.index = index; } @Override public String toString() { return "价格:" + price + " 重要度:" + important + " 索引:" + index; } } public static void main(String[] args) { Scanner in = new Scanner(System.in); String s = in.nextLine(); String[] split = s.split(" "); int amount = Integer.parseInt(split[0]); int productCount = Integer.parseInt(split[1]); ArrayList<Products> mainList = new ArrayList<>(); ArrayList<ArrayList<Products>> attachList = new ArrayList<>(); for (int i = 0; i < productCount; i++) { attachList.add(new ArrayList<>()); } int record = 0; for (int i = 0; i < productCount; i++) { String proStr = in.nextLine(); String[] proSplit = proStr.split(" "); int price = Integer.parseInt(proSplit[0]); int important = Integer.parseInt(proSplit[1]); int mainProductIndex = Integer.parseInt(proSplit[2]); int index = record; if (mainProductIndex == 0) { //存主件 Products products = new Products(price, important, index); mainList.add(products); } else { //存附件 if (attachList.get(mainProductIndex - 1) == null) { attachList.set(mainProductIndex - 1, new ArrayList<>()); } Products products = new Products(price, important, index); attachList.get(mainProductIndex - 1).add(products); } record++; } int[] dp = new int[amount + 1]; dp[0] = 0; for (int i = 0; i < mainList.size(); i++) { Products products = mainList.get(i); int price = products.price; int important = products.important; int index = products.index; for (int j = amount; j > 0; j--) { //如果为空只考虑加或不加主件 if (attachList.get(index).isEmpty()) { if (j >= price) { dp[j] = Math.max(dp[j], dp[j - price] + important * price); } } else { //如果有附件 先看看存不存主件 if (j >= price) { dp[j] = Math.max(dp[j], dp[j - price] + important * price); } //在分别考虑 主件+附件1 和 主件+附件2 要不要为dp[j构成满意度 for (int k = 0; k < attachList.get(index).size(); k++) { Products attachProducts = attachList.get(index).get(k); int attachPrice = attachProducts.price; int attachImportant = attachProducts.important; if (j >= price + attachPrice) { dp[j] = Math.max(dp[j], dp[j - price - attachPrice] + important * price + attachImportant * attachPrice); } } //如果有两个附件 还要考虑主件+两个附件 要不要为dp[j]构成满意度 if (attachList.get(index).size() > 1) { Products attachProducts1 = attachList.get(index).get(0); int attachPrice1 = attachProducts1.price; int attachImportant1 = attachProducts1.important; Products attachProducts2 = attachList.get(index).get(1); int attachPrice2 = attachProducts2.price; int attachImportant2 = attachProducts2.important; int sumPrice = price + attachPrice1 + attachPrice2; int sumImportant = important * price + attachImportant1 * attachPrice1 + attachImportant2 * attachPrice2; if (j >= sumPrice) { dp[j] = Math.max(dp[j], dp[j - sumPrice] + sumImportant); } } } } } System.out.println(dp[amount]); } }
这是一个典型的0-1 背包问题的变种,其中存在主件和附件的约束关系。我们需要使用动态规划来解决这个问题。
主件和附件的关系:
目标:
思路:
package com.shisui.medium; import java.util.*; public class shopping_list { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int N = sc.nextInt(); // 总钱数 int m = sc.nextInt(); // 物品个数 int[] dp = new int[N + 1]; // dp数组,dp[j]表示总价不超过j时的最大满意度 // 物品信息 Item[] items = new Item[m + 1]; List<Item>[] attachments = new List[m + 1]; // 初始化 for (int i = 0; i <= m; i++) { attachments[i] = new ArrayList<>(); } // 读取物品数据 for (int i = 1; i <= m; i++) { int v = sc.nextInt(); int p = sc.nextInt(); int q = sc.nextInt(); items[i] = new Item(v, p, q); if (q > 0) { attachments[q].add(items[i]); // 把附件添加到对应的主件附件列表中 } } // 动态规划求解 for (int i = 1; i <= m; i++) { if (items[i].q > 0) continue; // 处理附件时跳过 for (int j = N; j >= 0; j--) { // 从总钱数开始倒序遍历 // 主件选项 if (j >= items[i].v) { dp[j] = Math.max(dp[j], dp[j - items[i].v] + items[i].v * items[i].p); } // 主件+附件1选项 if (attachments[i].size() >= 1 && j >= items[i].v + attachments[i].get(0).v) { dp[j] = Math.max(dp[j], dp[j - items[i].v - attachments[i].get(0).v] + items[i].v * items[i].p + attachments[i].get(0).v * attachments[i].get(0).p); } // 主件+附件2选项 if (attachments[i].size() == 2 && j >= items[i].v + attachments[i].get(1).v) { dp[j] = Math.max(dp[j], dp[j - items[i].v - attachments[i].get(1).v] + items[i].v * items[i].p + attachments[i].get(1).v * attachments[i].get(1).p); } // 主件+附件1+附件2选项 if (attachments[i].size() == 2 && j >= items[i].v + attachments[i].get(0).v + attachments[i].get(1).v) { dp[j] = Math.max(dp[j], dp[j - items[i].v - attachments[i].get(0).v - attachments[i].get(1).v] + items[i].v * items[i].p + attachments[i].get(0).v * attachments[i].get(0).p + attachments[i].get(1).v * attachments[i].get(1).p); } } } System.out.println(dp[N]); } // 定义一个Item类来存储物品信息 static class Item { int v; // 价格 int p; // 重要度 int q; // 0表示主件,其他表示附件所属主件的编号 public Item(int v, int p, int q) { this.v = v; this.p = p; this.q = q; } } }
import java.util.*; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { // 物品类 private static class good{ public int v; // 物品价格 public int p; // 重要程度 public int q; // 是否是附件 public good a1 = null; public good a2 = null; public good(int v, int p, int q){ this.v = v; this.p = p; this.q = q; } public void setAppendGoods(good a){ if(this.a1 == null) this.a1 = a; else this.a2 = a; } public boolean isFollowed(){ if(this.a1 == null && this.a2 == null) return false; else return true; } } public static void main(String[] args) { Scanner in = new Scanner(System.in); int m = in.nextInt(); // 钱数 int n = in.nextInt(); // 购买物品数 List<good> input_good = new ArrayList<>(); // 存储输入值 List<good> input_follow_good = new ArrayList<>(); Map<Integer,Integer> map = new HashMap<Integer,Integer>(); // 存储主件位置 int totalCount = 0; // 记录主件原始数组中位置 int mainCount = 0; // 记录主件在主件数组中位置 while(in.hasNext()){ int v = in.nextInt(); int p = in.nextInt(); int q = in.nextInt(); if(q != 0) input_follow_good.add(new good(v, p, q)); else{ input_good.add(new good(v, p ,q)); map.put(totalCount,mainCount); mainCount++; } totalCount++; } int index = 0; // 插入辅件 if(!input_follow_good.isEmpty()){ for(good g:input_follow_good){ index = map.get(g.q-1); input_good.get(index).setAppendGoods(g); } } int len = input_good.size(); int[][] dp = new int[m+1][len+1]; // 边界条件 - dp[0][j] = 0 | dp[i][0] = 0 // 辅件计算 - 0 选1 选2 两个都选 // 状态转换方程 (不带辅件)dp[i][j] = Max(dp[i][j-1], dp[i-input[j][0]][j-1] + input[j][0] * input[j][1]) (携带辅件)dp[i][j] = Max(dp[i][j-1], dp[i-input[j][0]][j-1] + input[j][0] * input[j][1], dp[i-input[j][0]-input[j1][0]][j-1] + input[j][0] * input[j][1] +input[j1][0] * input[j1][1], ***) for(int i=1; i<m+1; i++){ for(int j=1; j<len+1; j++){ good temp = input_good.get(j-1); dp[i][j] = dp[i][j-1]; if(i-temp.v >= 0){ dp[i][j] = Math.max(dp[i][j-1], dp[i-temp.v][j-1]+temp.v*temp.p); if(temp.isFollowed()){ if(temp.a1 != null && i-temp.v-temp.a1.v >= 0) // 带附件1 dp[i][j] = Math.max(dp[i][j],dp[i-temp.v-temp.a1.v][j-1]+temp.v*temp.p+temp.a1.v*temp.a1.p); if(temp.a2 != null && i-temp.v-temp.a2.v >= 0) // 带附件2 dp[i][j] = Math.max(dp[i][j],dp[i-temp.v-temp.a2.v][j-1]+temp.v*temp.p+temp.a2.v*temp.a2.p); if(temp.a1 != null && temp.a2 != null && i-temp.v-temp.a1.v-temp.a2.v >= 0) // 带附件1和2 dp[i][j] = Math.max(dp[i][j],dp[i-temp.v-temp.a1.v-temp.a2.v][j-1]+temp.v*temp.p+temp.a1.v*temp.a1.p+temp.a2.v*temp.a2.p); } } } } System.out.println(dp[m][len]); } }
个人也是第一次接触算法这个领域,本身只是个搬砖的,如果解法有不好的地方,欢迎各位指出。
import java.util.ArrayList; import java.util.List; import java.util.Scanner; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { int limitMoney = in.nextInt(); int n = in.nextInt(); List<Goods> goodsList = new ArrayList<Goods>(); for (int i = 0; i < n; i++) { int price = in.nextInt(); int value = in.nextInt() * price; int type = in.nextInt(); Goods goods = new Goods(i + 1, price, value, type); goodsList.add(goods); } ShoppingPlan shoppingPlan = calculateMaxValue(goodsList, limitMoney); System.out.println(shoppingPlan.getTotalValue()); } } public static ShoppingPlan calculateMaxValue(List<Goods> goodsList, int limitMoney) { // 最大公约数 int gcd = getArrayGcd(goodsList); int n = goodsList.size(); int m = limitMoney / gcd; ShoppingPlan[][] dp = new ShoppingPlan[n + 1][m + 1]; // 初始化边界 dp[0][0] = new ShoppingPlan(); for (int i = 1; i <= n; i++) { dp[i][0] = dp[0][0]; } for (int j = 1; j <= m; j++) { dp[0][j] = dp[0][0]; } // 计算动态规划矩阵 for (int i = 1; i <= n; i++) { Goods goods = goodsList.get(i - 1); for (int j = 1; j <= m; j++) { // 构建新的购物方案 ShoppingPlan plan = new ShoppingPlan(); // 基础方案的索引值 int planIndex = j - goods.getPrice() / gcd; // 是否附件 if (goods.isAttachment()) { // 索引之需大于等于0,购买的钱才是足够的 if (planIndex >= 0) { // 获取主件 Goods mainGoods = goodsList.get(goods.getType() - 1); // 判断选取的基础方案是否有主件,决定是否添加主件 boolean hasMainGoods = dp[i - 1][planIndex].hasMainGoods(goods); if (!hasMainGoods) { // 如果选取的基础方案没有购买主件,则需要腾出购买主件的钱,需要将基础方案再往前推 planIndex -= mainGoods.getPrice() / gcd; } // 腾出钱之后,看看够不够买,如果不够,那就不使用基础方案了,使用新方案从头开始 if (planIndex >= 0) { // 前如果够那就复制一份基础方案 plan = dp[i - 1][planIndex].createSnapshot(); } // 然后再加上买主件 if (!hasMainGoods) { plan.addGoods(mainGoods); } } } else { // 主件处理 if (planIndex >= 0) { // 只需要判断钱够不够就行了,方案的索引大于0,就是够钱出方案,复制一份基础方案继续购买 plan = dp[i - 1][planIndex].createSnapshot(); } } // 无论主件附件都是需要加进方案判断的 plan.addGoods(goods); // 看看新生成的计划有没有超过目前的钱 if (plan.getTotalPrice() <= j * gcd) { // 如果钱够,则比较上个商品使用这么多钱的方案,然后再判断哪个方案的满意度高,择优获取 dp[i][j] = ShoppingPlan.max(dp[i - 1][j], plan); continue; } // 不够钱买新方案的,那就用上个商品在这个钱的方案 dp[i][j] = dp[i - 1][j]; } } // 将最终的方案返回 return dp[n][m]; } /** * 商品 */ public static class Goods { // 商品编号 private int id; // 商品单价 private int price; // 商品价值(满意度) private int value; // 商品类型(主件为0,附件大于0) private int type; public Goods(int id, int price, int value, int type) { this.id = id; this.price = price; this.value = value; this.type = type; } public int getId() { return this.id; } public int getPrice() { return this.price; } public int getValue() { return this.value; } public int getType() { return this.type; } /** * 商品是否附件 * @return true/false */ public boolean isAttachment() { return this.type > 0; } } /** * 购物方案 */ public static class ShoppingPlan { // 商品列表 private List<Goods> goodsList; // 总金额 private int totalPrice; // 总价值(总满意度) private int totalValue; /** * 构造函数,需要输入方案编号 */ public ShoppingPlan() { this.goodsList = new ArrayList<Goods>(); this.totalPrice = 0; this.totalValue = 0; } private ShoppingPlan(List<Goods> goodsList, int totalPrice, int totalValue) { this.goodsList = goodsList; this.totalPrice = totalPrice; this.totalValue = totalValue; } /** * 添加商品,同时会累计总金额和总价值 * @param goods 商品 */ public void addGoods(Goods goods) { this.goodsList.add(goods); this.totalPrice += goods.getPrice(); this.totalValue += goods.getValue(); } public int getTotalPrice() { return this.totalPrice; } public int getTotalValue() { return this.totalValue; } /** * 判断方案中是否存在当前附件的主件 * @param attachment 附件商品 * @return true/false */ public boolean hasMainGoods(Goods attachment) { for (Goods mainGoods : this.goodsList) { if (mainGoods.getId() == attachment.getType()) { return true; } } return false; } /** * 复制购物方案(不会影响到原购物方案信息) * @return 购物方案副本 */ public ShoppingPlan createSnapshot() { List<Goods> goodsList = new ArrayList<Goods>(this.goodsList.size()); goodsList.addAll(this.goodsList); return new ShoppingPlan(goodsList, this.totalPrice, this.totalValue); } /** * 获取满意度高的方案 * @param a 购物方案a * @param b 购物方案b * @return 满意度高的方案 */ public static ShoppingPlan max(ShoppingPlan a, ShoppingPlan b) { if (a.getTotalValue() >= b.getTotalValue()) { return a; } return b; } } /** * 用更相减损法计算公约数 * @param a 数a * @param b 数b * @return 最大公约数 */ public static int gcd(int a, int b) { if (a < b) { int tmp = a; a = b; b = tmp; } return (a % b == 0) ? b : gcd(a - b, b); } /** * 计算数组里的所有数的最大公约数 * @param goodsList 源数据数组 * @return 数组里的所有数的最大公约数 */ public static int getArrayGcd(List<Goods> goodsList) { int gcd = goodsList.get(0).getPrice(); for (int i = 1; i < goodsList.size(); i++) { gcd = gcd(gcd, goodsList.get(i).getPrice()); } return gcd; } }
这个题是背包问题的变种,基础解法是二维动态规划。不过,由于加了主件和附件的限制,需要判断的东西一下子多了很多。本人脑子不够用,就将有可能用到的西信息封装起来了,一共是两个类:ShoppingPlan(购物方案)和Goods(商品)
商品的基础属性是不会变的,通过调整购物方案里的商品列表做动态规划达到最优。
说一下动态规划二位数组的生成思路。
如果是一维的动态规划,我们可以知道它的思路是递推,也就是前面方案作为基础方案不断积累,数组的最后一个元素dp[n]就是最后的结果。一维的题,一般每一步都是一定会发生,且有规律的,只是发生的时间有交错。
沿着一维动态规划的思路拓展到二维,那就是经典的背包问题,按每件物品的选取累计,但是跟一维不同的是,物品可选可不选,这样就衍生出了分支,所以需要使用二维来记录分支。然后通过前两个物品选出来的方案作为基础,用上一个物品加入基础方案,和当前物品加入基础方案,这两个方案作比较,择优设置到当前的方案中。
背包数组的纵向是物品,横向记录了选取和不选取的值
回到商品选取,商品选取的话大致思路是按照背包问题构筑的,由于加入了主件附件联动,我们需要考虑以下问题:
1.当前的商品是否附件,如果是就需要跟主件一起计算金额
2.基础方案是否包含了主件,如果包含的话,就不能将主件再加到购物方案里面了,只能加附件
3.如果基础方案没有主件,那么我需要腾出钱去买主件,如果剩下的钱不够了,我还需要换一个基础方案,再加上主件附件,然后去比较
梳理之后,发现问题可以按以下思路解决:
1.判断商品是否附件,如果是主件,那么直接看钱够不够,然后跟上个商品在这个钱的旧方案比较就好
2.如果是附件,那么一直加钱,直到满足有一个基础方案+附件的新方案出来,如果没有,就用同价格上个商品的方案
3.判断基础方案中是否已存在主件,如果不存在主件的话,加上主件的价格,一直加钱,直到有一个基础方案+主件+附件的新方案出来,没有的话,用铜价格上个商品的方案
最后发现,同时满足钱够且满意度高才会替换方案,所以最后二维数组最后一个元素就是最优的购物方案。
由于按1块钱去构造数组和循环,会产生大量的同样的方案,且处理时间非常长,所以采用了求取商品数组的最大公约数简化数组的构成和缩短计算时间。
import java.util.Objects; import java.util.Scanner; public class Interview { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String input = scanner.nextLine(); String[] priceAndNum = input.split(" "); int price = Integer.parseInt(priceAndNum[0]); int num = Integer.parseInt(priceAndNum[1]); Goods[] goodsList = new Goods[num]; for (int i = 0; i < num; i++) { String goodsInfo = scanner.nextLine(); String[] goodsArray = goodsInfo.split(" "); Goods goods = new Goods(); goods.setPrice(Integer.parseInt(goodsArray[0])); goods.setSatisfy(Integer.parseInt(goodsArray[1])); goods.setAttachId(Integer.parseInt(goodsArray[2])); goodsList[i] = goods; } for (int i = 0; i < num; i++) { Goods goods = goodsList[i]; if (goods.attachId > 0) { Goods main = goodsList[goods.attachId - 1]; if (Objects.isNull(main.getFirstAttach())) { main.setFirstAttach(i); } else { main.setSecondAttach(i); } } } int [][] dp = new int[num + 1][price + 1]; //遍历所有商品 for (int i = 1; i <= num; i++) { Goods goods = goodsList[i - 1]; //4中情况 1:主件 2:主件+附件1 3:主件+附件2 4:主件+附件1+附件2 int mainSatisfy = goods.price * goods.satisfy; boolean ifAttach1 = Objects.nonNull(goods.firstAttach); boolean ifAttach2 = Objects.nonNull(goods.secondAttach); int attach1Price = ifAttach1 ? goodsList[goods.firstAttach].price : 0; int attach2Price = ifAttach2 ? goodsList[goods.secondAttach].price : 0; int attach1Satisfy = ifAttach1 ? mainSatisfy + goodsList[goods.firstAttach].satisfy * goodsList[goods.firstAttach].price : 0; int attach2Satisfy = ifAttach2 ? mainSatisfy + goodsList[goods.secondAttach].satisfy * goodsList[goods.secondAttach].price : 0; int attach1And2 = ifAttach2 && ifAttach1 ? attach1Satisfy + attach2Satisfy - mainSatisfy : 0; //遍历预算,动态找出满足价格范围内的最大满意度 for (int j = 1; j <= price; j++) { dp[i][j] = dp[i - 1][j]; if (goods.attachId > 0) { //如果是附件,那么不放dp,dp的值为放进一件、两件、三件...商品时的最大满意度 continue; } if (j - goods.price >= 0) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods.price] + mainSatisfy); } if (ifAttach1 && (j - (goods.price + attach1Price) >= 0)) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods.price - attach1Price] + attach1Satisfy); } if (ifAttach2 && (j - (goods.price + attach2Price) >= 0)) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods.price - attach2Price] + attach2Satisfy); } if (ifAttach1 && ifAttach2 && (j - (goods.price + attach1Price + attach2Price) >= 0)) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods.price - attach1Price - attach2Price] + attach1And2); } } } System.out.println(dp[num][price]); } static class Goods { private Integer price; private Integer satisfy; private Integer attachId; private Integer firstAttach; private Integer secondAttach; public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } public Integer getSatisfy() { return satisfy; } public void setSatisfy(Integer satisfy) { this.satisfy = satisfy; } public Integer getAttachId() { return attachId; } public void setAttachId(Integer attachId) { this.attachId = attachId; } public Integer getFirstAttach() { return firstAttach; } public void setFirstAttach(Integer firstAttach) { this.firstAttach = firstAttach; } public Integer getSecondAttach() { return secondAttach; } public void setSecondAttach(Integer secondAttach) { this.secondAttach = secondAttach; } } }
import java.util.*;
public class Main {
public static void main(String[] args) { Scanner in = new Scanner(System.in); int N = in.nextInt(); int m = in.nextInt(); int[] v = new int[m + 1]; //价格 int[] p = new int[m + 1]; //重要度 int[] q = new int[m + 1]; //所属主件编号 for (int i = 1 ; i 1 ; i++) { v[i] = in.nextInt(); p[i] = in.nextInt(); q[i] = in.nextInt(); } System.out.println(getMaxSat(v, p, q, N, m)); } //获取最大满意值方法 public static int getMaxSat(int[] v, int[] p, int[] q, int N, int m) { Good[][] good = new Good[m + 1][3]; //g[i][j]表示i号物品的第j个附件(最多2个),i=0表示i号商品是主件,g[i][0]为空表示i号商品为附件 int max = 0; int[] sat = new int [N + 1]; //sat[i]表示花费i元时的满意度 //跟据条件初始化good对象数组 for (int i = 1 ; i <= m ; i++) { Good gt = new Good(v[i], v[i] * p[i]); if (q[i] == 0) { good[i][0] = gt; } else { //是附件时判断是否存在另一个附件 if (good[q[i]][1] == null) good[q[i]][1] = gt; else good[q[i]][2] = gt; } } for (int i = 1; i <= m; i++) { for (int j = N; j >= 0; j--) { Good gt0 = good[i][0]; Good gt1 = good[i][1]; Good gt2 = good[i][2]; // i号商品不是主件时跳过循环 if (gt0 == null) break; // 第i号物品为主件时列出四种情况:主,主+附1,主+附2,主+附1+附2,并记录四种情况下的最大的满意度 else { //该主件没有附件 if (gt1 == null && gt2 == null) { if (gt0.v <= j) { //和原来的sat[j]比较,取最大满意度并记录 sat[j] = Math.max(sat[j], gt0.vp + sat[j - gt0.v]); } } //该主件有1个附件,若剩余金额允许购买主件和附件,比较主和主+附1的最大满意度 if (gt1 != null) { if (gt0.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + sat[j - gt0.v]); } if (gt0.v + gt1.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + gt1.vp + sat[j - gt0.v - gt1.v]); } } //该主件有2个附件,若剩余金额允许购买主件和附件,比较主,主+附1,主+附2,主+附1+附2的最大满意度 if (gt2 != null) { if (gt0.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + sat[j - gt0.v]); } if (gt0.v + gt1.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + gt1.vp + sat[j - gt0.v - gt1.v]); } if (gt0.v + gt2.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + gt2.vp + sat[j - gt0.v - gt2.v]); } if (gt0.v + gt1.v + gt2.v <= j) { sat[j] = Math.max(sat[j], gt0.vp + gt1.vp + gt2.vp + sat[j - gt0.v - gt1.v - gt2.v]); } } } } } return sat[N]; }
}
//定义商品类
class Good {
int v;//价格 int vp;//满意度 public Good(int v, int vp) { this.v = v; this.vp = vp; }
}
import java.util.ArrayList; import java.util.List; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer line = new StringTokenizer(br.readLine()); int money = Integer.parseInt(line.nextToken()); int num = Integer.parseInt(line.nextToken()); Good[] goods = new Good[num]; int i = 0; while (i < num) { line = new StringTokenizer(br.readLine()); int v = Integer.valueOf(line.nextToken()); int p = Integer.valueOf(line.nextToken()); int q = Integer.valueOf(line.nextToken()); if (goods[i] == null) { goods[i] = new Good(v, p, q); } else { goods[i].v = v; goods[i].p = p; goods[i].q = q; } if (q > 0) { // 附件 if (goods[q - 1] == null) { // 主件还未创建 goods[q - 1] = new Good(0, 0, 0); } if (goods[q - 1].g1 < 0) { goods[q - 1].g1 = i; } else { goods[q - 1].g2 = i; } } i++; } System.out.println(shopping(goods, money)); } public static int shopping(Good[] goods, int money) { int dp[] = new int[money + 1]; for (int i = 0; i < goods.length; i++) { Good g = goods[i]; for (int j = money; j >= g.v && g.q == 0; j--) { // 只买主件 dp[j] = Math.max(dp[j - g.v] + g.p * g.v, dp[j]); // 主 + 附1 if (g.g1 >= 0 && j >= (g.v + goods[g.g1].v)) { Good g1 = goods[g.g1]; dp[j] = Math.max(dp[j - g.v - g1.v] + g.p * g.v + g1.v * g1.p, dp[j]); } // 主 + 附2 if (g.g2 >= 0 && j >= (g.v + goods[g.g2].v)) { Good g2 = goods[g.g2]; dp[j] = Math.max(dp[j - g.v - g2.v] + g.p * g.v + g2.v * g2.p, dp[j]); } // 主 + 附1 + 附2 if (g.g1 >= 0 && g.g2 >= 0 && j >= (g.v + goods[g.g1].v + goods[g.g2].v)) { Good g1 = goods[g.g1]; Good g2 = goods[g.g2]; dp[j] = Math.max(dp[j - g.v - g1.v - g2.v] + g.p * g.v + g1.v * g1.p + g2.v * g2.p, dp[j]); } } } return dp[money]; } public static class Good { private int v; // 价格 private int p; // 重要性 private int q; // 主件id private int g1 = -1; // 附件1 private int g2 = -1; // 附件2 public Good(int v, int p, int q) { this.v = v; this.p = p; this.q = q; } } }
import java.util.Scanner; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int m = in.nextInt() / 10; int s = in.nextInt(); //最大数组长度 Good[] gs = new Good[s]; //组装基础数据 for (int i = 0; i < s ; i++) { int v = in.nextInt() / 10; int p = in.nextInt(); int q = in.nextInt(); //生成对象并按照顺序写入 gs[i] = new Good(v, p * v, q); } //二次循环写入附件信息 for (int i = 0; i < s ; i++) { Good good = gs[i]; if (!good.isMain) { Good parent = gs[good.q - 1]; if (parent.q1 == -1) { parent.q1 = i; } else { parent.q2 = i; } } } //结果集分值,物品 int[][] res = new int[m + 1][s + 1]; //一次循环物品 for (int i = 1; i < s + 1 ; i++) { //二次循环商品价值 for (int j = 0; j <= m ; j++) { Good good = gs[i - 1]; //附件数据忽略,取上一个物品的值 if (!good.isMain) { res[j][i] = res[j][i - 1]; } else { //赋默认值,上一个物品当前分值最优解 res[j][i] = res[j][i - 1]; //主物品剩余分值优解 if (good.v <= j) { res[j][i] = Math.max(res[j][i], good.p + res[j - good.v][i - 1]); } //主物品+q1+剩余分值最优解 if (good.q1 > -1 && good.v + gs[good.q1].v <= j) { int count = good.p + gs[good.q1].p + res[j - good.v - gs[good.q1].v][i - 1]; res[j][i] = Math.max(res[j][i], count); } //主物品+q2+剩余物品最优解 if (good.q2 > -1 && good.v + gs[good.q2].v <= j) { int count = good.p + gs[good.q2].p + res[j - good.v - gs[good.q2].v][i - 1]; res[j][i] = Math.max(res[j][i], count); } //主物品+两附件+剩余物品最优解 if (good.q1 > -1 && good.q2 > -1 && good.v + gs[good.q1].v + gs[good.q2].v <= j) { int count = good.p + gs[good.q1].p + gs[good.q2].p + res[j - good.v - gs[good.q1].v - gs[good.q2].v][i - 1]; res[j][i] = Math.max(res[j][i], count); } } } } //输出结果 System.out.println(res[m][s] * 10); } //商品对象 static class Good { //商品价值 public int v; //计算后的满意度 public int p; //是否为附件 public int q; //是否主件,默认不是 public boolean isMain = false; //附件编号 public int q1 = -1; public int q2 = -1; public Good(int v, int p, int q) { this.v = v; this.p = p; this.q = q; //提前判断好是否是主件 if (q == 0) { this.isMain = true; } } } }
我看过很多人写的代码,他们在“dp[i][j] = dp[i-1][j]”这里的处理都显得没有逻辑可言,下面的四个max判断大部分人第一条写的Math.max(dp[i][j],dp[i-1][j-v] + tempdp);,这样写逻辑根本无法自洽,同01背包问题分析,第一次如果你不买那么就是dp[i-1][j],买了就是dp[i-1][j-v] + tempdp,不存在说不买还是dp[i][j]的。 往下三条if这里之所以写dp[i][j]是因为它是同上一条计算出来的dp[i][j]的比较,是不同购买方案的比较。只要这里能理解透彻,我觉得这题大家就能懂了, 希望我下面代码对大家有所帮助。 import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int money = sc.nextInt(); int n = sc.nextInt(); if (n <= 0 || money <= 0) System.out.println(0); good[] Gs = new good[n + 1]; for (int i = 1; i <= n; i++) { int v = sc.nextInt();//物品价格 int p = sc.nextInt();//物品重要度 int q = sc.nextInt();//是否主件(主件编号) Gs[i] = new good(v, p, q); } for (int i = 1; i <= n; i++) { if (Gs[i] != null && Gs[i].q > 0) { //如果是附件,则给附件ID置位,方便后面计算附件方案 if (Gs[Gs[i].q].a1 == 0) { Gs[Gs[i].q].setA1(i); } else if (Gs[Gs[i].q].a2 == 0){ Gs[Gs[i].q].setA2(i); } } } int[][] dp = new int[n + 1][money + 1]; for (int i = 1; i <= n; i++) { int v = 0, v1 = 0, v2 = 0, v3 = 0, tempdp = 0, tempdp1 = 0, tempdp2 = 0, tempdp3 = 0; //只有主件 v = Gs[i].v; tempdp = Gs[i].p * v; //主件加附件1 if(Gs[i].a1 != 0){ v1 = Gs[Gs[i].a1].v + v; tempdp1 = tempdp + Gs[Gs[i].a1].v * Gs[Gs[i].a1].p; } //主件加附件2 if(Gs[i].a2 != 0){ v2 = Gs[Gs[i].a2].v + v; tempdp2 = tempdp + Gs[Gs[i].a2].v * Gs[Gs[i].a2].p; } //主件加附件1和附件2 if(Gs[i].a1 != 0 && Gs[i].a2 != 0){ v3 = Gs[Gs[i].a1].v + Gs[Gs[i].a2].v + v; tempdp3 = tempdp + Gs[Gs[i].a1].v * Gs[Gs[i].a1].p + Gs[Gs[i].a2].v * Gs[Gs[i].a2].p; } for(int j = 1; j <= money; j++){ if(Gs[i].q > 0) dp[i][j] = dp[i-1][j];//如果物品是附件,先不买 else if(Gs[i].q == 0){ dp[i][j] = j >= v? Math.max(dp[i-1][j],dp[i-1][j-v] + tempdp) : dp[i-1][j]; //Math.max(不买,买) : 钱不够 dp[i][j] = j >= v1? Math.max(dp[i][j],dp[i-1][j-v1] + tempdp1) : dp[i][j]; //此处dp[i][j]指的是上一行的dp[i][j],即比较不同购买方案,往下的dp[i][j]的值一直是被不断刷新的 dp[i][j] = j >= v2? Math.max(dp[i][j],dp[i-1][j-v2] + tempdp2) : dp[i][j]; dp[i][j] = j >= v3? Math.max(dp[i][j],dp[i-1][j-v3] + tempdp3) : dp[i][j]; } } } System.out.println(dp[n][money]); } /** * 定义物品类 */ private static class good { public int v; //物品的价格 public int p; //物品的重要度 public int q; //物品的主附件ID public int a1=0; //附件1ID public int a2=0; //附件2ID public good(int v, int p, int q) { this.v = v; this.p = p; this.q = q; } public void setA1(int a1) { this.a1 = a1; } public void setA2(int a2) { this.a2 = a2; } } }
import java.util.Scanner; public class HJ16 { /** * 【购物单】 * <p> * 描述:王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的 * 必须先买该附件所属的主件,且每件物品只能购买一次 * 每件物品规定了一个重要度,用整数 1 ~ 5 表示 * <p> * 要求:希望在花费不超过 N 元的前提下,使自己的满意度达到最大 * <p> * 【解题思路】:1.创建物品属性类v-价格,p-重要度,q-主件或附件(0或主件编号) * 2.将输入信息放入物品类 * 3.分四种,主件,主件+附件1,主件+附件2,主件+附件1+附件2,逐次算出满意度。 * 4.遍历这四种情况下的最优解,合并最优解。 */ public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 输入 int totalMoney = sc.nextInt();//总钱数 int goodNumber = sc.nextInt();//物品个数 Good[] goodArray = new Good[goodNumber + 1]; for (int i = 1; i <= goodNumber; i++) { Good good = goodArray[i]; if (good == null) { good = new Good(); } good.setV(sc.nextInt()); good.setP(sc.nextInt()); good.setQ(sc.nextInt()); if (good.getQ() > 0) { if (goodArray[good.getQ()] == null) { goodArray[good.getQ()] = new Good(); } //给主件赋值附件信息 if (goodArray[good.getQ()].getAttach1() == null) { goodArray[good.getQ()].setAttach1(good); } else { goodArray[good.getQ()].setAttach2(good); } } goodArray[i] = good; } int[][] dp = new int[goodNumber + 1][totalMoney + 1];//最终的满意度 dp[0][0] = 0; //每种情况的满意度数组 int[][] level = new int[goodNumber + 1][4]; int[][] costMoney = new int[goodNumber + 1][4]; for (int i = 1; i <= goodNumber; i++) { Good g = goodArray[i]; //只有主件 level[i][0] = g.getV() * g.getP(); costMoney[i][0] = g.getV(); //附件1 if (g.getAttach1() != null ) { level[i][1] = g.getV() * g.getP() + g.getAttach1().getV() * g.getAttach1().getP(); costMoney[i][1] = g.getV() + g.getAttach1().getV(); } //附件2 if (g.getAttach2() != null) { level[i][2] = g.getV() * g.getP() + g.getAttach2().getV() * g.getAttach2().getP(); costMoney[i][2] = g.getV() + g.getAttach2().getV(); } //附件1+附件2 if (g.getAttach1() != null && g.getAttach2() != null) { level[i][3] = g.getV() * g.getP() + g.getAttach1().getV() * g.getAttach1().getP() + g.getAttach2().getV() * g.getAttach2().getP(); costMoney[i][3] = g.getV() + g.getAttach1().getV() + g.getAttach2().getV(); } for (int j = totalMoney; j >= 0; j = j - 10) { for (int k = 0; k < costMoney[i].length; k++) { if (g.getQ() > 0) { dp[i][j] = dp[i - 1][j]; } else { if (costMoney[i][k] <= j) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - costMoney[i][k]] + level[i][k]); } else { dp[i][j] = dp[i - 1][j]; } } } } } System.out.println(dp[goodNumber][totalMoney]); } } class Good { private int v;//价格 private int p;//重要度 private int q;//主件or附件 private Good attach1;//附件1 private Good attach2;//附件2 public int getV() { return v; } public void setV(int v) { this.v = v; } public int getP() { return p; } public void setP(int p) { this.p = p; } public int getQ() { return q; } public void setQ(int q) { this.q = q; } public Good getAttach1() { return attach1; } public void setAttach1(Good attach1) { this.attach1 = attach1; } public Good getAttach2() { return attach2; } public void setAttach2(Good attach2) { this.attach2 = attach2; } }
import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; public class T16 { static int maxsumOfSatisfaction = 0; public static void main(String[] args) { HashMap<Integer, int[]> goods = new HashMap<>(); Scanner sc = new Scanner(System.in); String str = sc.nextLine(); String[] split = str.split(" "); int money = Integer.parseInt(split[0]); int number = Integer.parseInt(split[1]); for (int i = 0; i < number; i++) { String temp = sc.nextLine(); int[] detail = new int[3]; String[] split1 = temp.split(" "); detail[0] = Integer.parseInt(split1[0]); detail[1] = Integer.parseInt(split1[1]); detail[2] = Integer.parseInt(split1[2]); goods.put(i, detail); } orderByMo(goods); getAllCase(0, goods, new ArrayList<>(), money, 0, 0); System.out.println(maxsumOfSatisfaction); } private static void orderByMo(HashMap<Integer, int[]> goods) { for (int i = 0; i < goods.size(); i++) { int[] good = goods.get(i); if (good[2] > i && good[2] != 0) { //交换主件和附件的位置 goods.put(i, goods.get(good[2] - 1)); goods.put(good[2] - 1, good); //修改附件信息 for (int j = i + 1; j < goods.size(); j++) { int[] ints = goods.get(j); if (ints[2] == good[2]) { ints[2] = i + 1; goods.put(j, ints); } } } } } public static void getAllCase(int index, HashMap<Integer, int[]> goods, ArrayList<Integer> tempCase, int money, int sum, int sumOfSatisfaction) { if (sum > money) { return; } ArrayList<Integer> copy = new ArrayList<>(tempCase); if (index == goods.size()) { if (tempCase.size() != 0) { if (sumOfSatisfaction > maxsumOfSatisfaction) { maxsumOfSatisfaction = sumOfSatisfaction; } } } else { //选择购买这个商品 int[] good = goods.get(index); sum += good[0]; if (good[2] == 0) { tempCase.add(index); getAllCase(index + 1, goods, tempCase, money, sum, sumOfSatisfaction + good[0] * good[1]); } else { if (tempCase.contains(good[2] - 1)) { tempCase.add(index); getAllCase(index + 1, goods, tempCase, money, sum, sumOfSatisfaction + good[0] * good[1]); } } //选择不购买这个商品 getAllCase(index + 1, goods, copy, money, sum - good[0], sumOfSatisfaction); } } }
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int m = scanner.nextInt(); List<LinkedList<int[]>> goods = new ArrayList<>(m); for (int i = 0; i < m; i++) { goods.add(new LinkedList<>()); } for (int i = 0; i < m; i++) { int num1 = scanner.nextInt(); int num2 = scanner.nextInt(); int num3 = scanner.nextInt(); if (num3 == 0) { goods.get(i).addFirst(new int[]{num1, num2}); } else { goods.get(num3-1).addLast(new int[]{num1, num2}); } } //移出空的good goods.removeIf(AbstractCollection::isEmpty); int[] dp = new int[n + 1]; for (int i = 0; i < goods.size(); i++) { LinkedList<int[]> good = goods.get(i); for (int j = n; j >= good.get(0)[0]; j--) { //不管有没有附件,这步都要执行 dp[j] = Math.max(dp[j], dp[j - good.get(0)[0]] + good.get(0)[1] * good.get(0)[0]); if (good.size() > 1) { //选择一个附件,附件数大于1,选择一个附件这步都要执行 //选择一个附件有两种可能,选择附件1或者附件2,由于大于1 这里情况为选择附件1 if (j >= (good.get(0)[0] + good.get(1)[0])) { dp[j] = Math.max(dp[j], dp[j - good.get(0)[0] - good.get(1)[0]] + good.get(0)[1] * good.get(0)[0] + good.get(1)[1] * good.get(1)[0]); } } if (good.size() > 2) { //同上选择一个附件有两种可能,选择附件1或者附件2,由于大于2 这里情况为选择附件2 if (j >= (good.get(0)[0] + good.get(2)[0])) { dp[j] = Math.max(dp[j], dp[j - good.get(0)[0] - good.get(2)[0]] + good.get(0)[1] * good.get(0)[0] + good.get(2)[1] * good.get(2)[0]); } //选择两个附件 附件数大于2,选择两个附件这步都要执行 if (j >= (good.get(0)[0] + good.get(1)[0]+good.get(2)[0])) { dp[j] = Math.max(dp[j], dp[j - good.get(0)[0] - good.get(1)[0] - good.get(2)[0]] + good.get(0)[1] * good.get(0)[0] + good.get(1)[1] * good.get(1)[0]+good.get(2)[1] * good.get(2)[0]); } } } } System.out.println(dp[n]); } }先参考了一下评论区的思想,就是分成了不选择附件、选择一个附件、选择两个附件三种情况,独立写出😅。100%通过率