java實現計算最優現金優惠券組合
在眾多可疊加現金類型優惠券中(比如100減5,200減12等),選出可打折金額最大的組合。
下面代碼
package com.dk.common.util.algo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 計算最優優惠券組合
*
*/
public class CalcDiscountCouponOptimalCombination {
/// ***** 測試
public static void main(String[] args) {
double max = 600;
List<Item> list = new ArrayList<>();
list.add(new Item(100, 2));
list.add(new Item(100, 3));
list.add(new Item(300, 3));
list.add(new Item(300, 9));
list.add(new Item(300, 4));
list.add(new Item(200, 4));
list.add(new Item(100, 2));
list.add(new Item(200, 9));
list.add(new Item(300, 4));
list.add(new Item(100, 3));
list.add(new Item(230, 3));
list.add(new Item(300, 3));
list.add(new Item(500, 9));
list.add(new Item(300, 4));
list.add(new Item(100, 4));
list.add(new Item(100, 2));
list.add(new Item(400, 9));
list.add(new Item(300, 4));
list.add(new Item(100, 3));
list.add(new Item(230, 3));
for (int i = 0; i < 5; i++) {
list.add(new Item(300, 3));
list.add(new Item(500, 9));
list.add(new Item(300, 4));
list.add(new Item(100, 4));
list.add(new Item(100, 2));
list.add(new Item(400, 9));
list.add(new Item(300, 4));
list.add(new Item(100, 3));
list.add(new Item(230, 3));
list.add(new Item(230, 3));
}
// ************************
// 券數量多的,建議先按“折扣限制”從大到小排序,可以減少邏輯計算時間
list.sort((o1, o2) -> o1.deduct == o2.deduct ? 0 : o1.deduct > o2.deduct ? -1 : 1);
Map<String, Double> maxReduceIdxIssMap = calcMaxReduceIdxIssMap(list, max);
System.out.println(maxReduceIdxIssMap);
for (String idxIss : maxReduceIdxIssMap.keySet()) {
System.out.println("-------------------------------------");
System.out.println(idxIss);
String[] idxIsArr = idxIss.split("");
for (int idx = 0; idx < idxIsArr.length; idx++) {
String is = idxIsArr[idx];
if ("1".equals(is)) {
System.out.println(idx + ": " + list.get(idx));
}
}
}
}
/**
* 計算最大優惠組合列表
*/
public static Map<String, Double> calcMaxReduceIdxIssMap(List<Item> list, double maxAstrict) {
Map<String, Double> maxReduceIdxIssMap = new HashMap<>();// 統計極限組合
int len = list.size();
LoopRear lr = new LoopRear() {
double currMaxReduce = 0;
// idxIss 由0、1組成,1所在下標表示對應項參與了此組合
@Override
public boolean runRear(int start, String idxIss, double deductSum, double reduceSum) {
boolean haveRear = false;// 后面流程有沒有符合條件的子類組合
String pad = "1";
for (int i = start; i < len; i++) {
Item item = list.get(i);
double currDeductSum = deductSum + item.deduct;// 當前組合折扣限制總和
double currReduceSum = reduceSum + item.reduce;// 當前組合折扣額度總和
if (currDeductSum <= maxAstrict) {
haveRear = true;// 當前組合限制總和小於總金額,表示當前組合的父類不是極限組合(存在子類組合)
String currIdxIss = idxIss + pad;
boolean haveRearRear = this.runRear(i + 1, currIdxIss, currDeductSum, currReduceSum);// 是否存在子類組合
if (!haveRearRear) {
// 不存在子類組合,表示當前組合為一個極限組合
// 比對之前的折扣金額,只保留最大折扣金額的
if (currReduceSum > currMaxReduce) {
currMaxReduce = currReduceSum;
maxReduceIdxIssMap.clear();
maxReduceIdxIssMap.put(currIdxIss, currReduceSum);
} else if (currReduceSum == currMaxReduce) {
maxReduceIdxIssMap.put(currIdxIss, currReduceSum);
}
}
}
pad = "0" + pad;// 前面下標的項不參與之后組合,后移一位
}
return haveRear;
}
};
lr.runRear(0, "", 0, 0);// 從第一個開始執行
return maxReduceIdxIssMap;
}
public static class Item {
protected double deduct;// 折扣限制,折扣條件
protected double reduce;// 折扣額度
public Item(double deduct, double reduce) {
super();
this.deduct = deduct;
this.reduce = reduce;
}
@Override
public String toString() {
return reduce + "[" + deduct + "]";
}
}
protected interface LoopRear {
boolean runRear(int start, String idxIss, double deductSum, double reduceSum);
}
}