近期由于公司搞话费充值活动,话费都是三网有固定额度,例如面额 100,50,30,20,10,1-9 等等(有时可能移动,电信能充值这些面额,联通可能充不了20,10,1-9)。用户在充值操作中,有时是个性化的充值,例如充值60,160,165等,那么问题来了,要根据手机号运营商找到对应面额,进行最大拆分。
*先解释下个人理解的最大拆分,例如160元的拆分有多种,例如(100,50,10)(160个1元)(100,30,30)之类,其中(100,50,10)是叫做最大拆分(这个涉及成本问题,面额大的优惠力度也大)。
现在正题来了,经过无数次算法的研究和计算,最终应用树递归左遍历模式,进行计算。先上个图
例如,把160 根据 [100,50,30,10] 进行最大拆分
图中可见,
1)除根节点外,每个节点的分支都是小于该节点数值的剩余队列。
2)根据树结构左遍历方式,会先找到100剩余60,60再继续找到50剩余10,10继续找到30,无法继续,回溯到同层10,发现正好整除,既遍历结束,最大拆分组被找到。
代码献上
方法代码:
public static boolean getFeeList2(List<Integer> myFeeList,int myFee,List<Integer> feeList){
for (Integer fee : feeList) {
//先判断充值额度是否大于面额度,不能下一个面额
if(myFee<fee){
continue;
}
//计算整除数和余数
int zct = myFee/fee;
int yt = myFee%fee;
if(yt==0){//余数为0,代表正好能整除,则到了根节点
for(int i=0;i<zct;i++){
myFeeList.add(fee);
}
return true;
}else{//余数不为0
//递归出口,判断是否为叶子节点,是返回
if(feeList.size()==1){
return false;
}
//获取该节点子节点序列
List<Integer> subList = feeList.subList(1, feeList.size());
//求出剩余充值额度
int bFee=myFee-fee;
//再次往下遍历树结构
boolean feeList2 = getFeeList2(myFeeList, bFee, subList);
if(feeList2){
myFeeList.add(fee);
return true;
}else{
continue;
}
}
}
return false;
}
测试代码:
public static void main(String[] args) {
//最终拆分组
List<Integer> myFeeList = new ArrayList<Integer>();
//面值
List<Integer> feelist = new ArrayList<Integer>();
feelist.add(100);
feelist.add(50);
feelist.add(30);
feelist.add(10);
feelist.add(6);
feelist.add(1);
//充值额度
int myFee=160;
//寻找最大拆分组,如果能拆分返回true,不能拆分返回false
boolean feeList2 = BillFileUtils.getFeeList2(myFeeList, myFee, feelist);
System.out.println(feeList2);
//能拆分打印拆分后的队列
for (Integer integer : myFeeList) {
System.out.println(integer);
}
}
测试结果图:(160)
测试结果图:(60)