使用场景

  1. 有一些报销额度,需要用自己发票的金额去抵扣;
  2. 如果发票金额总和超过报销额度,只能报销最大额度的金额;
    比如:报销额度1000,发票金额1200,那么可以报销1000
  3. 现在的发票也可以留给下次报,所以你需要凑出符合额度的发票,但不想让金额超出太多;
  4. 此程序效率低,但你的时间比CPU的时间值钱很多。

效果

代码

from itertools import combinations

intro = """====================================
===@凑发票计算器
===@Yaodo
===@https://www.imtrq.com
====================================
"""
print(intro)

s = input("输入现有的每张发票金额,以空格键分隔,按回车键结束:\n")
target = float(input("输入目标金额,按回车键结束:\n"))
li = [float(x) for x in s.split()]
# 最多需要多少张发票
def get_max_num(li, target):
    li.sort()
    for n in range(1, len(li)+1):
        if sum(li[:n]) >= target:
            return n
    return 0
# 最少需要多少张发票
def get_min_num(li, target):
    li.sort(reverse=True)
    for n in range(1, len(li)+1):
        if sum(li[:n]) >= target:
            return n
    return 0
# 如果选择n张发票,返回金额最接近的组合
def try_a_num(n, li, target):
    comb = list(combinations(li, n))
    answer = []
    for i in comb:
        if sum(i)>=target:
            answer.append((sum(i), len(i), i))
        if len(answer)==0:
            return (0,0,0)
        else:
            answer.sort()
    return answer[0]
# 每个n试一次
def work(li, target):
    max_num = get_max_num(li, target)
    min_num = get_min_num(li, target)
    if max_num*min_num==0:
        return 0
    answer = []
    for n in range(min_num, max_num+1):
        answer.append(try_a_num(n, li, target))
    return answer
# 输出结果
answer = work(li, target)
if answer==0 or answer==[]:
    print("错误!无法凑出所需金额。")
else:
    print("\n====================================\n方案列表\n====================================")
    answer.sort()
    i = 1
    for x in answer:
        output = '{}\t总金额:{:.2f}\t发票张数:{}\t组合:{}'.format(i, x[0], x[1], x[2])
        print(output)
        i+=1