描述
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件 电脑 打印机,扫描仪 书柜 图书 书桌 台灯,文具 工作椅 无 如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第i件物品的价格为]v[i],重要度为w[i],共选中了k件物品,编号依次为1,j2,...,jk,则满意度为: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说明:
由第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.*;
import java.math.*;
import java.util.stream.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
Map<Integer, Pg> map = new HashMap<>();
Map<Integer, List<Pg>> sonMap = new HashMap<>();
List<Pg> list = new ArrayList<>();
StringBuilder builder = new StringBuilder();
List<Integer> prices = new ArrayList<>();
builder.append(a+" " + b +" ");
for (int i = 0; i < b; i++) {
int o = in.nextInt();
int p = in.nextInt();
int q = in.nextInt();
builder.append(o + " " + p + " " + q + " ");
Pg pg = new Pg();
pg.price = o;
prices.add(o);
pg.val = p;
pg.num = i + 1;
if (q > 0) {
pg.isSon = true;
pg.parentNum = q;
if (sonMap.containsKey(q)) {
sonMap.get(q).add(pg);
}
else {
List<Pg> tmpList = new ArrayList<>();
tmpList.add(pg);
sonMap.put(q, tmpList);
}
} else {
pg.isSon = false;
pg.sons = new ArrayList<Pg>();
map.put(pg.num, pg);
}
list.add(pg);
}
// 公约数:用来缩短二维数组总钱数N的长度
int gong = getGong(prices, a);
map.forEach((k, v) -> {
if (sonMap.containsKey(k)) {
v.sons = sonMap.get(k);
}
});
List<Pg> srcList = map.values().stream().collect(Collectors.toList());
int max = deal(srcList, a, gong);
System.out.println(max);
}
}
public static int getGong(List<Integer> list, int a) {
int res = 10;
while (res <= a) {
final int val = res;
int count = (int)list.stream().filter(item -> item%val !=0).count();
if (count > 0) {
res = res/10;
return res;
}
res = res*10;
}
return res > a ? res/10 : res;
}
/**
动态规划算法
*/
public static int deal(List<Pg> srcList, int a, int gong) {
int width = srcList.size();
int size = a/gong;
int[][] dp = new int[size+1][width+1];
// int[] dp = new int[size+1];
for (int i=0; i<=width; i++) {
dp[0][i] = 0;
}
for (int i=0; i<=size; i++) {
dp[i][0] = 0;
}
for (int i=1; i<=size; i++) {
for (int j=1; j<=width; j++) {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j]);
List<ObjVal> list = getPriceSum(srcList.get(j-1), gong);
for (ObjVal obj : list) {
if (i - obj.sumPrice >= 0) {
for (int k=1; k<=j; k++) {
dp[i][j] = Math.max(dp[i][j], obj.sumVal+dp[i-obj.sumPrice][k-1]);
}
}
}
}
}
int max = 0;
for (int j=1; j<=width; j++) {
max = Math.max(max, dp[size][j]);
}
return max;
}
/**
* 计算主件及附件各种场景下所需的总钱数,场景有:单主件、主件+任意一个附件、主件+两个附件
*/
public static List<ObjVal> getPriceSum(Pg pg, int gong) {
int sum = pg.price;
int sumV = pg.price * pg.val;
int j=0;
int size = 1;
List<ObjVal> list = new ArrayList<ObjVal>();
ObjVal obj = new ObjVal();
obj.sumPrice = sum/gong;
obj.sumVal = sumV;
list.add(obj);
if (pg.sons != null && pg.sons.size()>0) {
size = pg.sons.size();
}
else {
return list;
}
int basicP = sum;
int basicV = sumV;
while (j<pg.sons.size()) {
int tmpPrice = pg.sons.get(j).price;
int tmpVal = pg.sons.get(j).val;
sum += tmpPrice;
sumV += tmpPrice*tmpVal;
obj = new ObjVal();
obj.sumPrice = sum/gong;
obj.sumVal = sumV;
list.add(obj);
if (j == 1) {
obj = new ObjVal();
obj.sumPrice = (basicP+tmpPrice)/gong;
obj.sumVal = basicV + tmpPrice*tmpVal;
list.add(obj);
}
j++;
}
return list;
}
public static class ObjVal {
int sumPrice;
int sumVal;
}
/**
递归处理
*/
public static void deal(List<Pg> list, Map<String, TreeSet<Pg>> map, int index,
Map<Integer, Pg> parentMap, int a) {
if (a <= 0 || index == list.size()) {
return ;
}
Pg pg = list.get(index);
if (pg.price > a) {
deal(list, map, index + 1, parentMap, a);
return ;
}
Set<String> keys = map.keySet();
Map<String, TreeSet<Pg>> mapTmp = new HashMap<>();
for (String key : keys) {
TreeSet<Pg> set = map.get(key);
set = copySet(set);
if (!pg.isSon && pg.price <= a) {
// 保证顺序
set.add(pg);
mapTmp.put(getSetKey(set), set);
deal(list, map, index + 1, parentMap, a - pg.price);
} else {
if (pg.isSon) {
Pg parent = parentMap.get(pg.parentNum);
if (set.contains(parent) && pg.price <= a) {
// 保证顺序
set.add(pg);
mapTmp.put(getSetKey(set), set);
deal(list, map, index + 1, parentMap, a - pg.price);
} else {
if (pg.price + parent.price <= a) {
set.add(pg);
set.add(parentMap.get(pg.parentNum));
mapTmp.put(getSetKey(set), set);
deal(list, map, index + 1, parentMap, a - pg.price);
} else {
deal(list, map, index + 1, parentMap, a);
}
}
}
}
}
map.putAll(mapTmp);
TreeSet<Pg> tmp = new TreeSet<Pg>();
if (pg.isSon) {
Pg parent = parentMap.get(pg.parentNum);
if (pg.price + parent.price <= a) {
tmp.add(pg);
tmp.add(parentMap.get(pg.parentNum));
map.put(getSetKey(tmp), tmp);
deal(list, map, index + 1, parentMap, a - parent.price - pg.price);
} else {
deal(list, map, index + 1, parentMap, a);
}
} else {
if (pg.price <= a) {
// 保证顺序
tmp.add(pg);
map.put(getSetKey(tmp), tmp);
deal(list, map, index + 1, parentMap, a - pg.price);
} else {
deal(list, map, index + 1, parentMap, a);
}
}
}
public static int getSetSumPrice(TreeSet<Pg> res) {
return res.stream().mapToInt(item->item.price).sum();
}
public static int getSetSum(TreeSet<Pg> res) {
return res.stream().mapToInt(item->item.price * item.val).sum();
}
public static String getSetKey(TreeSet<Pg> res) {
return res.stream().map(item->String.valueOf(item.num)).collect(
Collectors.joining("##"));
}
public static TreeSet<Pg> copySet(TreeSet<Pg> res) {
TreeSet<Pg> tset = new TreeSet<>();
for (Pg pg : res) {
tset.add(pg.copy());
}
return tset;
}
public static class Pg implements Comparable<Pg> {
// 是否附件:true附件;false主件;
boolean isSon;
// 价格
Integer price;
// 重要度
int val;
// 序号
int num;
// 如果是附件,所属主件序号
int parentNum;
// 如果是主件,附件集合
List<Pg> sons;
public Pg copy() {
Pg pg = new Pg();
pg.isSon = this.isSon;
pg.price = this.price;
pg.val = this.val;
pg.num = this.num;
pg.parentNum = this.parentNum;
return pg;
}
public int compareTo(Pg pg) {
return this.num - pg.num;
}
}
}