双十一有很多促销活动,比如“满200元减50元”。假如购物车有 n 个(n>100)商品,要从中选几个,在凑够满减条件前提下,让选出的商品价格总和最大程度地接近满减条件(200)元。这怎么使用代码解决呢?
这时候可以使用动态规划的方式, 代码如下:
import copy
import random
def solution(items, n, w, amt):
tmp = [False for _ in range(3 * w + 1)]
states = [copy.deepcopy(tmp) for _ in range(n)]
states[0][0] = True
if items[0] <= 3 * w:
states[0][items[0]] = True
for i in range(1, n):
# 不放
for j in range(3 * w + 1):
if states[i - 1][j] is True:
states[i][j] = True
# 放
j = 0
while j <= 3 * w - items[i]:
if states[i - 1][j] is True:
states[i][j + items[i]] = True
j += 1
j = w
while j < 3 * w + 1:
if states[n - 1][j] is True:
break
j += 1
if j == 3 * w + 1:
print('没有可行解')
return False, 9999999999
sum_all = 0
cnt = 0
for i in range(n - 1, 0, -1):
if j - items[i] >= 0 and states[i - 1][j - items[i]] is True:
print(f'买它: {items[i] / dividend} (第{i + 1}个商品)')
j -= items[i]
sum_all += items[i]
cnt += 1
if j != 0:
print(f'买它: {items[0] / dividend} (第1个商品)')
sum_all += items[0]
cnt += 1
all_save_money = w / test_w * amt
avg_money = (sum_all - w / test_w * amt) / cnt
print(f"达到满减的条件: {w / dividend}")
print(f"总满减金额: {all_save_money / dividend}")
print(f"实际花费: {(sum_all - all_save_money) / dividend}")
print(f"购买件数: {cnt}")
print(f"商品均价: {avg_money / dividend}")
print(f"差额: {(sum_all - w) / dividend}")
return True, (sum_all - w) / dividend, avg_money / dividend
if __name__ == '__main__':
# 可重复购买的商品(用于凑单的商品)
candidates = [136.4, 25]
test_items = [random.choice(candidates) for _ in range(20)]
# 限制购买数量的商品(要多少买多少的商品)
limited_items = [34, 29, 28, 28, 29]
test_items += limited_items
test_n = len(test_items)
test_w = 199
test_amt = 25
min_diff = 999999999
idx = -1
lowest_avg_money = 9999999
lowest_avg_idx = -1
# 适配价格有小数点的
is_float = False
for item in candidates:
if isinstance(item, float):
test_items = [int(each * 100) for each in test_items]
test_w *= 100
test_amt *= 100
is_float = True
break
if is_float:
dividend = 100
else:
dividend = 1
print(f"商品价格表:\n{[item / dividend for item in test_items]}\n")
# 凑单满减次数
max_times = 10
for idx in range(1, max_times):
print(f'满足{idx}次满减: {(test_w * idx) / dividend}元')
res = solution(test_items, test_n, test_w * idx, test_amt)
if not res[0]:
break
if min_diff > res[1]:
min_diff = res[1]
idx = idx
if lowest_avg_money > res[2]:
lowest_avg_money = res[2]
lowest_avg_idx = idx
print()
print("结论:")
print(f"在第 {idx} 轮找到最小差额: {min_diff / dividend}")
print(f"在第 {lowest_avg_idx} 轮找到最小单价: {lowest_avg_money / dividend}")
实际效果:
商品价格表:
[136.4, 136.4, 136.4, 25.0, 136.4, 25.0, 25.0, 25.0, 136.4, 25.0, 136.4, 25.0, 136.4, 136.4, 25.0, 25.0, 136.4, 25.0, 136.4, 25.0, 34.0, 29.0, 28.0, 28.0, 29.0]
满足1次满减: 199.0元
买它: 29.0 (第25个商品)
买它: 34.0 (第21个商品)
买它: 136.4 (第19个商品)
达到满减的条件: 199.0
总满减金额: 25.0
实际花费: 174.4
购买件数: 3
商品均价: 58.13333333333333
差额: 0.4
满足2次满减: 398.0元
买它: 29.0 (第25个商品)
买它: 28.0 (第24个商品)
买它: 28.0 (第23个商品)
买它: 29.0 (第22个商品)
买它: 34.0 (第21个商品)
买它: 25.0 (第20个商品)
买它: 25.0 (第18个商品)
买它: 25.0 (第16个商品)
买它: 25.0 (第15个商品)
买它: 25.0 (第12个商品)
买它: 25.0 (第10个商品)
买它: 25.0 (第8个商品)
买它: 25.0 (第7个商品)
买它: 25.0 (第6个商品)
买它: 25.0 (第4个商品)
达到满减的条件: 398.0
总满减金额: 50.0
实际花费: 348.0
购买件数: 15
商品均价: 23.2
差额: 0.0
满足3次满减: 597.0元
买它: 29.0 (第25个商品)
买它: 34.0 (第21个商品)
买它: 25.0 (第20个商品)
买它: 136.4 (第19个商品)
买它: 25.0 (第18个商品)
买它: 136.4 (第17个商品)
买它: 25.0 (第16个商品)
买它: 25.0 (第15个商品)
买它: 136.4 (第14个商品)
买它: 25.0 (第12个商品)
达到满减的条件: 597.0
总满减金额: 75.0
实际花费: 522.2
购买件数: 10
商品均价: 52.22
差额: 0.2
满足4次满减: 796.0元
买它: 29.0 (第25个商品)
买它: 28.0 (第24个商品)
买它: 28.0 (第23个商品)
买它: 29.0 (第22个商品)
买它: 136.4 (第19个商品)
买它: 136.4 (第17个商品)
买它: 136.4 (第14个商品)
买它: 136.4 (第13个商品)
买它: 136.4 (第11个商品)
达到满减的条件: 796.0
总满减金额: 100.0
实际花费: 696.0
购买件数: 9
商品均价: 77.33333333333333
差额: 0.0
满足5次满减: 995.0元
买它: 29.0 (第25个商品)
买它: 34.0 (第21个商品)
买它: 25.0 (第20个商品)
买它: 136.4 (第19个商品)
买它: 25.0 (第18个商品)
买它: 136.4 (第17个商品)
买它: 25.0 (第16个商品)
买它: 25.0 (第15个商品)
买它: 136.4 (第14个商品)
买它: 136.4 (第13个商品)
买它: 25.0 (第12个商品)
买它: 136.4 (第11个商品)
买它: 25.0 (第10个商品)
买它: 25.0 (第8个商品)
买它: 25.0 (第7个商品)
买它: 25.0 (第6个商品)
买它: 25.0 (第4个商品)
达到满减的条件: 995.0
总满减金额: 125.0
实际花费: 870.0
购买件数: 17
商品均价: 51.1764705882353
差额: 0.0
满足6次满减: 1194.0元
买它: 28.0 (第24个商品)
买它: 25.0 (第20个商品)
买它: 136.4 (第19个商品)
买它: 25.0 (第18个商品)
买它: 136.4 (第17个商品)
买它: 25.0 (第16个商品)
买它: 136.4 (第14个商品)
买它: 136.4 (第13个商品)
买它: 136.4 (第11个商品)
买它: 136.4 (第9个商品)
买它: 136.4 (第5个商品)
买它: 136.4 (第3个商品)
达到满减的条件: 1194.0
总满减金额: 150.0
实际花费: 1044.2
购买件数: 12
商品均价: 87.01666666666667
差额: 0.2
满足7次满减: 1393.0元
买它: 29.0 (第25个商品)
买它: 136.4 (第19个商品)
买它: 136.4 (第17个商品)
买它: 136.4 (第14个商品)
买它: 136.4 (第13个商品)
买它: 136.4 (第11个商品)
买它: 136.4 (第9个商品)
买它: 136.4 (第5个商品)
买它: 136.4 (第3个商品)
买它: 136.4 (第2个商品)
买它: 136.4 (第1个商品)
达到满减的条件: 1393.0
总满减金额: 175.0
实际花费: 1218.0
购买件数: 11
商品均价: 110.72727272727272
差额: 0.0
满足8次满减: 1592.0元
买它: 28.0 (第24个商品)
买它: 25.0 (第20个商品)
买它: 136.4 (第19个商品)
买它: 25.0 (第18个商品)
买它: 136.4 (第17个商品)
买它: 25.0 (第16个商品)
买它: 25.0 (第15个商品)
买它: 136.4 (第14个商品)
买它: 136.4 (第13个商品)
买它: 25.0 (第12个商品)
买它: 136.4 (第11个商品)
买它: 25.0 (第10个商品)
买它: 136.4 (第9个商品)
买它: 25.0 (第8个商品)
买它: 25.0 (第7个商品)
买它: 136.4 (第5个商品)
买它: 136.4 (第3个商品)
买它: 136.4 (第2个商品)
买它: 136.4 (第1个商品)
达到满减的条件: 1592.0
总满减金额: 200.0
实际花费: 1392.0
购买件数: 19
商品均价: 73.26315789473684
差额: 0.0
满足9次满减: 1791.0元
没有可行解
结论:
在第 9 轮找到最小差额: 0.0
在第 2 轮找到最小单价: 0.2319999999999999999