多包放多个物品,判断是否放的下

前言

    最近做需求,碰到使用叠加券的情况。一笔订单,对于同一类型的优惠券可以叠加使用,但需要满足此订单匹配到的多个规则,规则有金额限制。
    转换问题描述:目前有多个物品,物品需要放在多个背包中。一个物品不可分割到多个包中,背包容量大小需要大于物品重量。有的背包只允许放一个物品,即使他的容量可以容纳下多个物品。有的背包允许放多个物品,只要他的容量能容纳下。现在需要判断多个背包能否放下多个物品,并返回如何放。

算法实现

package com.ykq;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author: ykq
 * @date: 2022/9/29 10:15
 * @Description:
 * 需求需要对于同一coupId的可叠加面额券,能够叠加使用
 * 同一coupId,对于不同的商品可能会过滤出不同的规则(多个背包),
 * 需要判断同一coupId的多张面额券(不可拆分),匹配到对应的多个规则(不可合并,如果合并,意味着一张券使用时匹配了两个规则)
 * 简单来说,多个背包(不可合并),放下多个物品(不可拆分)
 * 注意:不允许叠加使用的规则(背包),只允许匹配一张面额券(物品)
 */
@Slf4j
public class MultiPackUtil {

    /**
     * @Author ykq
     * @Date 2022/9/29 10:23
     * @Description 校验多个背包,能否放下多个物品
     */
    public static boolean checkPutDown(List<Package> parkList, List<Goods> goodsList) {
        // 将背包容量从大到小排序
        Collections.sort(parkList);
        // 将物品重量从大到小排序
        Collections.sort(goodsList);
        BigDecimal parkSum = parkList.stream().map(Package::getCapacity)
                .filter(Objects::nonNull).reduce(BigDecimal::add).get();
        BigDecimal goodsSum = goodsList.stream().map(Goods::getWeight)
                .filter(Objects::nonNull).reduce(BigDecimal::add).get();
        // 先校验包的总容量,物品总重量的大小
        if (parkSum.compareTo(goodsSum) < 0) {
            log.error("背包总容量限制:{} < 物品总重量:{}", parkSum, goodsSum);
            return false;
        }
        // 下面场景都是,包容量和 > 物品重量和
        // 如果只有一个包
        if (parkList.size() == 1) {
            Package park = parkList.get(0);
            // 不允许叠加使用,但有多个物品,则放不下
            if (!park.isOverLayUse() && goodsList.size() > 1) {
                log.error("只有一个背包:{}, 背包不允许放多个物品, 但物品有多个, 物品数量:{}", park.getParkId(), goodsList.size());
                return false;
            }
            // 其他场景,可以放下
            // 包括一个包,不允许叠加使用,有一个物品场景
            // 包括一个包,允许叠加使用,有多个物品场景
            // 将物品放入包中
            parkList.get(0).getGoodsList().addAll(goodsList);
            return true;
        }
        // 未使用物品栈
        LinkedList<Goods> goodsDeque = new LinkedList<>(goodsList);
        // 已使用物品栈
        LinkedList<Goods> removeGoodsDeque = new LinkedList<>();
        return checkPutDown2(parkList, goodsDeque, removeGoodsDeque);
    }

    /**
     * @Author ykq
     * @Date 2022/9/30 10:18
     * @Description 回溯法,递归判断是否能放下
     */
    private static boolean checkPutDown2(List<Package> parkList, LinkedList<Goods> goodsDeque,
                                  LinkedList<Goods> removeGoodsDeque) {
        // 如果有多个包,使用回溯法,一个个放
        for (int parkDos = 0; parkDos < parkList.size(); parkDos++) {
            Package park = parkList.get(parkDos);
            while(!goodsDeque.isEmpty()) {
                // 如果包总容量-已使用容量>物品重量
                // 获取栈顶元素
                Goods goods = goodsDeque.peekFirst();
                // 判断
                // 1.包不可放多个物品,没放物品时,包容量大于物品重量
                // 2.包可放物品,剩余包容量大于物品重量
                // 上述两种场景,可将物品放入包中
                if ((!park.isOverLayUse() && park.getGoodsList().isEmpty() && park.getCapacity().compareTo(goods.getWeight()) >= 0)
                    || (park.isOverLayUse() && park.getCapacity().subtract(park.getUsedWeight()).compareTo(goods.getWeight()) >= 0)){
                    // 将物品放入到包中
                    park.getGoodsList().add(goods);
                    // 已用容量增加
                    park.setUsedWeight(park.getUsedWeight().add(goods.getWeight()));
                    // 移除已使用的物品
                    goodsDeque.removeFirst();
                    // 将移除的物品放入被使用栈
                    removeGoodsDeque.addFirst(goods);
                    // 将后面的物品从头开始放
                    if (checkPutDown2(parkList, goodsDeque, removeGoodsDeque)) {
                        return true;
                    } else {
                        // 将被移除的元素,回滚到物品栈中
                        Goods removeGoods = removeGoodsDeque.removeFirst();
                        park.getGoodsList().remove(removeGoods);
                        goodsDeque.addFirst(removeGoods);
                        break;
                    }
                } else {
                    // 放不下就去下一个包
                    break;
                }
            }
            // 物品放完了,返回true
            if (goodsDeque.isEmpty()) {
                return true;
            }
        }
        return false;
    }

    /**
     * @Author ykq
     * @Date 2022/10/14 9:50
     * @Description 背包类
     */
    @Data
    public class Package implements Comparable<Package> {
        // 背包id
        private Long parkId;

        // 背包容量
        private BigDecimal capacity;

        // 是否允许放多个物品
        private boolean overLayUse;

        // 已放物品重量
        private BigDecimal usedWeight = BigDecimal.ZERO;

        // 放了哪些物品
        private List<Goods> goodsList = new ArrayList<>();

        /**
         * @Author ykq
         * @Date 2022/10/14 9:59
         * @Description 按背包不允许->允许排序,再按背包容量从大到小排序
         */
        @Override
        public int compareTo(Package other) {
            int thisOverLayUseInt = this.overLayUse ? 1 : 0;
            int otherOverLayUseInt = other.overLayUse ? 1 : 0;
            int order = thisOverLayUseInt - otherOverLayUseInt;
            if (order != 0) {
                return order;
            }
            return other.capacity.compareTo(this.capacity);
        }
    }

    /**
     * @Author ykq
     * @Date 2022/10/14 9:51
     * @Description 物品类
     */
    @Data
    public class Goods implements Comparable<Goods> {
        // 物品id
        private Long goodsId;

        // 物品重量
        private BigDecimal weight;

        /**
         * @Author ykq
         * @Date 2022/10/14 9:58
         * @Description 按物品重量从大到小排序
         */
        @Override
        public int compareTo(Goods other) {
            return other.weight.compareTo(this.weight);
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校多宝校务管理系统是一款专门为幼儿园和培训学校设计的行业应用软件。它融入了先进的协同管理理念,运用领先的信息化、网络化处理技术,并结合丰富的教育培训行业经验,解决了幼儿园和培训学校日常工作中的关键应用问题。该系统包含多个模块,可以实现管理员和学生的不同功能需求。管理员可以进行管理员信息的添加和修改,学生信息的添加和修改,课程的开设和查询,成绩的录入、统计和修改,以及个人密码的修改等操作。而学生可以选择课程,查询课程,显示课表,查询成绩单,修改个人密码等。\[1\]\[2\] 校多宝校务管理系统通过提高学校教务、市场、前台、销售、行政、财务等模块的工作效率,满足了用户的多种需求,并适应了学校信息交流、整体应变、构架调整和模块整合的要求。未来,该系统有望实现学校的无纸化办公环境。\[3\] 所以,学校教务管理系统使用C#进行开发,并采用了C#TreeView实现主题,使用Sql Server数据库进行数据存储。\[2\] #### 引用[.reference_title] - *1* *3* [基于C#、bootstap、三层架构 培训机构教务管理系统](https://blog.csdn.net/FXH1989/article/details/81236359)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于C#的学生综合教务管理系统](https://blog.csdn.net/newlw/article/details/124960829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值