优惠券组合小例子
优惠券的使用贯穿于我们生活的日常。"美团外卖"和"饿了么"成为大学时代的难忘记忆!如何精准凑单是门技术活,如何精准凑单以让自己获得最大满减优惠是众多小伙伴的一大需求。本文以一个小例子来实现对凑单这一场景的再现。
input1:点选的n道菜肴对应的单价列表quotion=[v1,v2,v3,…,vn];
input2:满减的优惠券额度man_N_jian;
output:最优的凑单匹配选项对应价格
为实现代码的简便,默认list中元素位置与菜肴名称一一对应。
算法时间复杂度:O(2^n)。
空间复杂度:O(n*2^n)。
- 算法思想:
- 价格列表quotion中存在man_N_jian额度,直接返回man_N_jian表示True
- 选择每一道菜肴本身是一个0-1整数规划,那么使用n个0或1的组合序列(falgArray)来与对应菜肴单价相乘加即可获得一个和数total,即: total =vi*falgArray[i],i=1,2,…n。如果和数total能先落在区间[man_N_jian, man_N_jian+5]内,然后计算delta=abs(total-man_N_jian),并保留当前的最小值-----min1,历史的最小值-----min2。将每一轮计算的三元组(total, falgArray,min2)存入list中以备最后找出最小差值对应的菜肴组合序列。
- 代码
#本质上就是一个0-1整数规划
#简易菜单
#quotion={'红烧牛肉':15, '红烧鱼':11, '可乐鸡翅':2, '牛肉':8,'北京烤鸭':3,'白菜':24,'花生':6,'西兰花':7,'土豆':32,'农家小炒肉':2,'羊肉':9}#,'西红柿炒蛋':20}
def isMixDiscount(quotion,man_N_jian,n):
#quotion=quotion1.values()
min1=0
min2=5
#可以凑成满减的最低额度
if man_N_jian in quotion:
binary_array.append(man_N_jian)
return True,binary_array
#不可以凑成满减的最低额度
else:
#算法复杂度(2^n)
for item in range(1,pow(2,n)):
total=0
falgArray=[]
falgArray=list(map(int,bin(item)[2:])) #转为二进制
for j in range(n-len(falgArray)): #填0补齐
falgArray.append(0)
for k in range(len(falgArray)):
total+=quotion[k]*falgArray[k]
if (man_N_jian<=total) and (total<man_N_jian+5):
min1=abs(total-man_N_jian)
if min1<min2:
min2=min1
binary_array.append((total,falgArray,min2))
else:
pass
else:
continue
#找出最小满减方案
min3=min([binary_array[i][2] for i in range(len(binary_array))])
result=[binary_array[i][0:2] for i in range(len(binary_array)) if binary_array[i][2]==min3 ] #返回点餐的菜品价格
return [quotion[i] for i in range(len(result[0][1])) if result[0][1][i]==1],result
#这里出现错误:原因是字典的items是无序的。
#result=isMixDiscount(list(map(lambda x:x[1],quotion.items())),man_N_jian,n)
binary_array=[]
n=int(input('n='))
man_N_jian=eval(input('man_N_jian='))
import time
start=time.time()
#caidan=[15, 11,24,6, 2, 8,3,7,32,2,9,4,20]
#caidan2与caidan1一一对应.
caidan2=['红烧牛肉', '红烧鱼', '可乐鸡翅', '牛肉','北京烤鸭','白菜','花生','西兰花','土豆','农家小炒肉','羊肉']#,'西红柿炒蛋':20}
caidan1=[15, 11,24,6, 2, 8,3,7,32,2,9,4,60,45,36,90]
while True:
if n<len(caidan1):
quotion=caidan1
result=isMixDiscount(quotion,man_N_jian,n)
print('最优解:',result)
#返回菜名
#记录菜名的索引列表
cailist=[]
for i in range(len(result[1][0][1])):
if result[1][0][1][i]==1:
cailist.append(i)
#[caidan[index1] for index1,index2 in list(zip(cailist,result[0]))]
item=[(caidan2[index1],index2) for index1,index2 in list(zip(cailist,result[0]))]
#[(caidan[result[1][0][1].index(index)]) for index in result[1][0][1] ]
print('菜肴信息:',item)
break
else:
print('对于n=请输入%d以内的某个整数' % len(caidan1))
print('对于man_N_jian=请输入100以内的某个整数')
n=int(input('n='))
man_N_jian=eval(input('man_N_jian='))
print('titaltime:',time.time()-start)
- 代码效果
#情况1:man_N_jian在单价列表quotion中
n=11
man_N_jian=20
最优解: (True, [20])
titaltime: 0.001999378204345703
#情况2:man_N_jian不在在单价列表quotion中,即需要自由组合
#例子1:
n=5
man_N_jian=20
最优解: ([15, 6], [(21, [1, 0, 0, 1, 0])])
titaltime: 0.0010056495666503906
#例子2:
n=8
man_N_jian=20
最优解: ([15, 2, 3], [(20, [1, 0, 0, 0, 1, 0, 1, 0])])
titaltime: 0.0020046234130859375
#例子3:
n=14
man_N_jian=100
最优解: ([15, 11, 24, 8, 3, 7, 32], [(100, [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0])])
titaltime: 0.08823323249816895
#例子4:
n=17
man_N_jian=100
对于n=请输入16以内的某个整数
对于man_N_jian=请输入100以内的某个整数
n=14
man_N_jian=100
最优解: ([15, 11, 24, 8, 3, 7, 32], [(100, [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0])])
titaltime: 7.192155838012695
例5:
n=14
man_N_jian=100
最优解: ([15, 11, 24, 8, 3, 7, 32], [(100, [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0])])
菜肴信息: [('红烧牛肉', 15), ('红烧鱼', 11), ('可乐鸡翅', 24), ('白菜', 8), ('花生', 3), ('西兰花', 7), ('土豆', 32)]
titaltime: 0.11931896209716797