import java.util.List; public class SplitOrders { public class Item{ /** * 卖家用户id */ long sellerId; /** * 商品价格,单位分 */ long price; } public class Order{ /** * 该订单对应的商品 */ List<Item> orderItems; /** * 该订单金额,单位分 */ long totalPrice; /** * 该订单对应的卖家userId */ long sellerId; } /** * 根据购物车的商品,生成相应的交易订单,根据如下规则 * 1.每笔交易订单可以有多个商品 * 2.每笔交易订单的商品只能是同一个卖家 * 3.每笔交易商品的总价格不能超过1000元 * 4.生成的交易订单数量最小 * @param items:购物车所有商品 */ public List<Order> packageItemsToOrders(List<Item> items){ return null; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub } }
其实就是一个最优拼单的问题
我的解决思路就是尽量的让一个订单金额趋向于1000,比较简单粗暴
具体思路
1.按照不同卖家进行分组
2.核心原则:通过凑单的方式,将每个订单的总金额趋向1000
将卖家下的商品列表按照价格排序 拆分为一个低价商品列表(升序) 高价商品列表(降序),
3.最高价商品+最低价商品,如果大于1000,将最商品单独高价 作为一个订单,移除列表,最低价商品放回列表,重复上述操作
4.最高价商品+最低价商品,如果小于1000,循环获取低价列表,与之前结果相加,大于1000则终止,小于1000则继续,
每次获取列表的商品都会通过remove方法,如果商品拼单失败就会原位置放回列表
具体代码如下
// 按照卖家进行分组
public List<Order> separationItems(List<Item> items) { Map<Long, List<Item>> map = new HashMap<>(); /** * 按照卖家进行分组, */ for (Item item : items) { Long sellId = item.sellerId; List<Item> sellerList = map.get(sellId); if (sellerList == null || sellerList.size() == 0) { sellerList = new ArrayList<>(); } sellerList.add(item); map.put(sellId, sellerList); } List<Order> res = new ArrayList<>(); /** * 以卖家为单位,合并订单 */ for (Long sellId : map.keySet()) { List<Order> orderMergeRes = mergeItems(sellId,map.get(sellId)); res.addAll(orderMergeRes); } return res; } /** * 商品列表合并操作 * 尽量保证每个订单的总额无限接近1000,将最高价格商品与最低价格相加,如果大于1000 * 则任务最高价格商品智能 * 以下面为例 * 12,34,45,67,222,555,777,888,999 * 1.999+12,大于1000,则 999 独立为一个订单 * 2.继续将888+12,小于1000,则继续+34,大于1000,将888 34 合并作为一个订单 * * @param lessThenT * @return */ public List<Order> mergeItems(Long sellId, List<Item> lessThenT) { Collections.sort(lessThenT); int len = lessThenT.size(); int flag = len % 2 == 0 ? len / 2 : len / 2 + 1; // 保存低价商品列表 从小到大排序 List<Item> minList = new ArrayList<>(); // 保存高价商品列表 从大到小 List<Item> maxList = new ArrayList<>(lessThenT); for (int i = 0; i < flag; i++) { minList.add(lessThenT.get(i)); maxList.remove(0); } Collections.reverse(maxList); List<Order> orderList = new ArrayList<>(); redo: while (maxList.size() > 0) { Item max = maxList.remove(0); Item min = minList.size()==0?null:minList.remove(0); /** * 最高价格商品 无法与其他商品合并为一单 * 或当前低价格已经被消耗完了 */ if ((min == null || max.price + min.price > 1000)) { /** * 合并失败,将商品还回到原处 */ if(min != null){ minList.add(0, min); } List<Item> tmp = new ArrayList<>(); tmp.add(max); Order order = new Order(); order.orderItems = tmp; order.totalPrice = max.price; order.sellerId = sellId; orderList.add(order); continue; } else { /** * 如果当前最高价格可以与最低价格合并,则再尝试 与次低价格商品合并 */ List<Item> tmp = new ArrayList<>(); tmp.add(max); tmp.add(min); Long total = max.price + min.price; redoLow: while (minList.size() > 0) { Item minSecond = minList.remove(0); if ((total + minSecond.price) > 1000) { minList.add(0, minSecond); Order order = new Order(); order.orderItems = tmp; order.totalPrice = total; order.sellerId = sellId; orderList.add(order); // 高价商品提前消耗完了 將低价商品列表 拆分 重复上一步 if (maxList.size() == 0 && minList.size() > 0) { maxList.add(0, minList.remove(minList.size() - 1)); } continue redo; } else { tmp.add(minSecond); total = total + minSecond.price; } // 低价商品提前被消耗完,将最小的 高价格商品移除到低价格中 if (minList.size() == 0 && maxList.size() > 0) { minList.add(0, maxList.remove(maxList.size() - 1)); continue redoLow; } } Order order = new Order(); order.orderItems = tmp; order.totalPrice = total; order.sellerId = sellId; orderList.add(order); } } return orderList; }