华为OD机试高频真题 Java实现【带分析注解】- 网上商城优惠活动

该问题是一个关于设计算法来帮助用户在购物时以最少的优惠券获取最优的价格。优惠券类型包括满减、打折和无门槛,每次购物可最多使用两种优惠券,且同类型优惠券需一次性全部使用。算法需要考虑各种优惠券组合和使用顺序,以找到最优惠的价格。对于每种购物金额,程序会计算并输出最低价格及所用优惠券总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 题目描述:网上商城优惠

[背景]
某网上商城举办优惠活动,发布了满减、打折、无门槛3种优惠券,分别为:
1.每满100元优惠10元,无使用数限制,如100~199元可以使用1张减10元,200~299可使用2张减20元,以此类推;
2.92折券,1次限使用1张,如100元,则优惠后为92元;
3.无门槛5元优惠券,无使用数限制,直接减5元。
[优惠券使用限制]
每次最多使用2种优惠券,2种优惠可以叠加(优惠加时以优惠后的价格计算》,以购物200元为例,可以先用92折券优惠到184元,再用1张满减券优惠10元,
最终价格是174元,也可以用满减券2张优惠20元为180元,再使用92折券优惠到165(165.6向下取整)元,不同使用顺序的优惠价格不同,以最优惠价格为准。
在一次购物中,同类型优惠券使用多张时必须一次性使用,不能分多次拆开穿插使用(不允许先使用1张满减券,再用打折券,再使用一张满减券)。
[问题]
请设计实现一种解决方法,帮助购物者以最少的优惠券获得最优的优惠价格。优惠后价格越低越好,同等优惠价格,使用的优惠券越少越好,可以允许某次购物不使用优惠券.
[约定]
优惠活动每人只能参加一次,每个人的优惠券种类和数量是一样的

输入描述:
第一行:每个人拥有的优惠券数量(数量取值范为[0,10]),按满减、打折、无门槛的顺序输入。第二行:表示购物的人数n (1<= n<= 10000)最后n行:
每一行表示某个人优惠前的购物总价格 (价格取值范围(0,10000],都为整数)。约定:输入都是符合题目设定的要求的。
输出描述:
每行输出每个人每次购物优惠后的最低价格以及使用的优惠券总数量,每行的输出顺序和输入的顺序保持一致。
补充说明:
1.优惠券数量都为整数,取值范围为[0,10].
2.购物人数为整数,取值范围为[1,10000]
3.优事券的购物总价为整数,取值范围为(0, 10000]。
4.优惠后价格如果是小数,则向下取整,输出都为整数。


示例1
输入:
3 2 5
3
100
200
400

输出:
65 6
155 7
338 4
说明:
[输入说明]
第一行:3种优患券数量分别为满减券3张,打折券2张,无门槛5张
第二行:总共3个人购物
第三行:第一个人购物优惠前价格为100元
第四行:第二个人购物优惠前价格为200元
第五行:第三个人购物优惠前价格为400元
[输出说明]
输入3个人,输出3行结果,同输入的顺序,对应每个人的优惠结果,如下:
第一行: 先使用1张满减券优惠到90元,再使用5张无门槛券优惠25元, 最终价格是65元,总共使用6张优惠券
第二行:先使用2张满减券优惠到180元,再使用5张无门槛券优惠25元,最终价格是155元,总共使用7张优惠卷
第三行: 先使用1张92折券优惠到368元,再使用3张满减券优惠30元,最终价格是338元,总共使用4张优惠券












public class Demo13 {
    public static void main(String[] args) {
        /**
         * 分析,总共有几种打折方式? 3种打折方式只能一起用两种,
         * 因为无门槛是直接减,不像打折是越高金额的时候打折效果最好,满减要求要每满100才减。。所以无门槛一定要在打折和满减之后使用
         * 所有有以下几种打折方式一起使用
         * 1、先满减再打折
         * 2、先满减再无门槛
         * 3、先打折再满减
         * 4、先打折再无门槛
         *
         * 还有 优惠券数量(数量取值范为[0,10])
         * 所以要考虑某种券为0的情况
         * 这里只考虑打折券是否为0,如果打折券为0了,那就只有 2、先满减再无门槛,这一种优惠券使用方式了
         *
         * 数据结构和算法分析:
         * 把每种情况之后的金额和使用打折券数量都存下来,用map存,key存金额,value存数量。 先比较金额,相同金额就比较数量存value,
         * 最后遍历map,把key最小的当作最优解, 把map存到list, 最后遍历打印list
         */
        Scanner sc = new Scanner(System.in);
        // 3种打折券的数量
        int manjianNum = sc.nextInt();
        int dazheNum = sc.nextInt();
        int wumenkanNum = sc.nextInt();

        // 参加购物人数
        int peopleNum = sc.nextInt();

        // 每个人的购物金额,打折前的
        int moneyA = sc.nextInt();
        int moneyB = sc.nextInt();
        int moneyC = sc.nextInt();

        // 打折前金额列表
        List<Integer> beforeMoneyList = new ArrayList<>();
        beforeMoneyList.add(moneyA);
        beforeMoneyList.add(moneyB);
        beforeMoneyList.add(moneyC);

        for (int i = 0; i < beforeMoneyList.size(); i++) {
            // 把金额都转成double再计算, 这里对金额精度要求不高,可以用double
            double money = beforeMoneyList.get(i);
            Map<Integer, Integer> map = new HashMap<>();
            // 把每种打折后的方式都计算一遍用map存下来,存的同时就可以比较数量,如果存在相同的金额,就存数字小的
            // 有打折才用以下3种,而且只能用一张打折券
            int manjianDazhe = 0;
            int dazheManjian = 0;
            int dazheWumenkan = 0;
            if(dazheNum > 0) {
                // 1、先满减再打折
                manjianDazhe = (int) Math.floor(manjian(money, manjianNum) * 0.92);
                // 使用的卷数量
                int manjianDazheNum = (int)  Math.floor(Math.min(money  / 100, manjianNum));
                map.put(manjianDazhe, manjianDazheNum + 1);
                // 3、先打折再满减
                dazheManjian = (int)  Math.floor(manjian(money * 0.92, manjianNum));
                // 使用的卷数量
                int dazheManjianNum = (int)  Math.floor(Math.min(money * 0.92 / 100, manjianNum));
                if (map.containsKey(dazheManjian)) {
                    map.put(dazheManjian, Math.min(map.get(dazheManjian), dazheManjianNum + 1));
                } else {
                    map.put(dazheManjian, dazheManjianNum + 1);
                }
                // 4、先打折再无门槛
                dazheWumenkan = (int) Math.floor((money * 0.92 - wumenkanNum * 5));
                if (map.containsKey(dazheWumenkan)) {
                    map.put(dazheWumenkan, Math.min(map.get(dazheWumenkan), wumenkanNum + 1));
                } else {
                    map.put(dazheWumenkan, wumenkanNum + 1);
                }
            }
            // 2、先满减再无门槛
            int manjianWumenkan = (int)  Math.floor((manjian(money, manjianNum) - wumenkanNum * 5));
            if (map.containsKey(manjianWumenkan)) {
                map.put(manjianWumenkan, Math.min(map.get(manjianWumenkan), wumenkanNum + (int)  Math.floor(Math.min(money / 100, manjianNum))));
            } else {
                map.put(manjianWumenkan, wumenkanNum + (int)  Math.floor(Math.min(money / 100, manjianNum)));
            }
            // 取出最小key
            int minMoney;
            if (dazheNum > 0) {
                minMoney = Math.min(Math.min(manjianDazhe, dazheManjian), Math.min(dazheWumenkan, manjianWumenkan));
            } else {
                minMoney = manjianWumenkan;
            }
            System.out.println(minMoney + " " + map.get(minMoney));
        }
    }

    /**
     * 满减单独抽出来,满减要判断金额/100数量和满减券数量
     */
    private static double manjian(double money, int manjianNum) {
        if (money / 100 < manjianNum) {
            return money - (Math.floor(money / 100)) * 10; // double除数还要做向下取整
        } else {
            return money - manjianNum * 10;
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值