利用动态规划解决购物中的最佳搭配问题

问题

商品总价 4800,共有以下几种充值赠送活动,问怎么充值购买最划算且最方便?

  • 充值 200 赠送 10
  • 充值 300 赠送 15
  • 充值 500 赠送 25
  • 充值 1000 赠送 50
  • 充值 2000 赠送 100
  • 充值 5000 赠送 250

实现


import java.util.*;

/**
 * 商品总价 4800,问怎么充值购买最划算且最方便?
 * 有以下优惠方案:
 * 充值 200 赠送 10
 * 充值 300 赠送 15
 * 充值 500 赠送 25
 * 充值 1000 赠送 50
 * 充值 2000 赠送 100
 * 充值 5000 赠送 250
 *
 * @author 杨贤达
 * @date 2019-11-13
 */
public class Solution {

    private static final Map<Integer, Integer> SOURCE = new LinkedHashMap<>();

    private static final int TARGET = 4800;

    private static Integer minNum = TARGET;

    // 只是为了简单去重用,未实现排序逻辑
    private static Set<List<Integer>> resultSet = new TreeSet<>((l1, l2) -> l1.containsAll(l2) && l2.containsAll(l1) ? 0 : 1);

    static {
        SOURCE.put(200, 10);
        SOURCE.put(300, 15);
        SOURCE.put(500, 25);
        SOURCE.put(1000, 50);
        SOURCE.put(2000, 100);
        SOURCE.put(5000, 250);
    }

    public static void main(String[] args) {
        fun(new ArrayList<>(), 0);
        printResult(resultSet);
    }

    private static void printResult(Set<List<Integer>> resultSet) {
        for (List<Integer> list : resultSet) {
            Map<Integer, Integer> map = new HashMap<>();
            for (Integer integer : list) {
                if (map.containsKey(integer)) {
                    map.put(integer, map.get(integer) + 1);
                } else {
                    map.put(integer, 1);
                }
            }
            Integer actualPayment = list.stream().reduce(0, Integer::sum);
            Integer totalAmount = list.stream().reduce(0, (a, b) -> a + b + SOURCE.get(b));
            System.out.println("==================================================");
            System.out.println("搭配集合 = " + map);
            System.out.println("实际支付 = " + actualPayment);
            System.out.println("实际支付 + 赠送金额 = " + totalAmount);
            System.out.println("浪费金额 = " + (totalAmount - TARGET));
            System.out.println("操作次数 = " + list.size());
        }
    }

    private static void fun(List<Integer> list, int total) {
        int waste = total - TARGET;
        // 浪费超过 150 元,直接不予考虑
        if (waste > 150) {
            return;
        }
        // 必须大于指定金额,不能小于,即浪费的金额必须为正数
        if (waste > 0 && waste < minNum) {
            minNum = waste;
        }
        // 收集筛选后的结果
        if (total - TARGET > 0 && total - TARGET < 150 && list.size() < 6) {
            resultSet.add(list);
        }
        for (Map.Entry<Integer, Integer> entry : SOURCE.entrySet()) {
            Integer key = entry.getKey();
            Integer value = entry.getValue();

            list.add(key);

            List<Integer> copy = (List<Integer>) ((ArrayList<Integer>) list).clone();
            total = total + key + value;
            fun(copy, total);

            list.remove(key);
            total = total - key - value;
        }
    }
}

输出

==================================================
搭配集合 = {2000=2, 200=3}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
==================================================
搭配集合 = {2000=2, 200=2, 300=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=1, 500=1, 1000=2, 200=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=2, 500=1, 200=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 4
==================================================
搭配集合 = {2000=2, 200=3}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
==================================================
搭配集合 = {2000=1, 500=1, 1000=2, 200=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=1, 1000=2, 300=2}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
==================================================
搭配集合 = {2000=2, 300=2}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 4
==================================================
搭配集合 = {2000=2, 200=2, 300=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=1, 1000=2, 300=2}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
==================================================
搭配集合 = {2000=2, 200=3}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
==================================================
搭配集合 = {2000=2, 200=2, 300=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=1, 500=1, 1000=2, 200=1}
实际支付 = 4700
实际支付 + 赠送金额 = 4935
浪费金额 = 135
操作次数 = 5
==================================================
搭配集合 = {2000=2, 200=3}
实际支付 = 4600
实际支付 + 赠送金额 = 4830
浪费金额 = 30
操作次数 = 5
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值