算法:购物单

描述

王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

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

如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。

每个主件可以有 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;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值