电影兑换券的推荐策略——二分图最优匹配算法

本文介绍了如何利用Kuhn-Munkres(KM)算法解决用户在购买多张电影票时,如何选择兑换券以实现最低实际支付金额的问题。在考虑了优先级规则(实际支付金额少、面值小、券包消耗快、过期时间早)后,通过贪心、暴力枚举和KM算法进行优化。最终,通过调整算法,实现了在有限的电影票数量下,快速找到最佳的兑换券组合,确保了效率和用户省钱的目标。

动手点关注 干货不迷路 👆

问题概述

一笔订单最多可使用所含电影票数目张兑换券。换而言之,用户选了几个座位,最多便能使用几张兑换券,兑换券有三个属性,分别是:

  • 面值(元):在不支持补差的情况下,票价小于等于面值才可以使用

  • 固定支付金额(元):满足兑换券的使用条件下,需要支付的钱。

  • 补差(是 / 否):如果支持补差,当票价大于面值时,还需要额外支付 (票价 - 面值)元

举个栗子:小明有一张面值 50,固定支付 19 元且支持补差的兑换券。那么他能使用这张兑换券去购买票价小于等于 50 元的电影票,只需支付 19 元。因为支持补差,所以他能购买票价为 60 元(大于面值)的电影票,需支付(19 + ( 60 - 50))既 29 元。

我们问题是:用户下了一笔订单,订单中有 x(根据业务场景,x <= 6)张电影票,y 张兑换券,从这 y 张兑换券中选择不超过 x 张兑换券,使得该笔订单的实际支付金额最少,如果有多种解决方案,那么根据以下优先级为用户推荐选券的方案:

  • 优先级 1: 选择实际支付金额少的方案

  • 优先级 2: 如果实际支付金额一致,则优先使用面值小的方案

    • 原因:如果用户想购买一张票价为 38 元的电影票,当前他有一张 40 元和一张 60 元的兑换券,任意使用一张兑换券能得到的实际支付金额都是 0 元,那么优先为用户选择 40 元的兑换券,这样 60 元的兑换券就能服务于用户的下一笔订单,更能为用户省钱。

  • 优先级 3: 若面值大小也一致,则优先使用优惠券所在券包消耗券数目多的优惠券

  • 优先级 4: 若消耗券数目一致,则优先使用过期时间早的优惠券

技术方案

方案一:贪心

具体步骤:排序

对票价从大到小排序,让票价高的电影票优先选择,目的就是为了金额大的电影票优先使用限制条件少(既面值大)的兑换券,让券面值得到充分利用,每个票和券的组合都尽可能是最优解。

证明方法:举反例法

很不幸,很快就找出一个反例推翻了这个方案,反例如图所示

342f3a9b78866b1aca5bc739db49da8e.png

方案二:暴力枚举

枚举所有方案,从这所有方案中求出最优解,但是时间复杂度高达 C(y, x) * A(x, x) 也就是 O(n!),若按照 1s 钟计算机能运行 10^8 次计算这样的标准,当 n = 13 的时候,需要超过 10s 才能得到答案,并且 n 每增加 1,时间就会扩大 n 倍。

解决方案三:二分图最优匹配算法——KM 算法

km(Kuhn-Munkres)算法简介

km 算法是一种二分图最佳匹配算法,该算法主要用于解决一个经典的问题模型:完美婚姻问题。该问题的描述如下:n 个男生和 n 个女生相亲,第 i 个男生和第 j 个女生在一起的幸福值是 val(i, j),如何让 n 个男生和 n 个女生完成一一配对,使得这个整体的总幸福值最大。我们的问题和完美婚姻问题模型有点相似,并且经过调研 km 算法时间复杂度是 n^3,km 算法有一个非常大的优点就是,他可以求出哪张券用于哪张电影票,适用于选座相关的业务场景。

km 算法落地(对 km 算法不熟悉的同学可以先浏览第三部分)

我们把兑换券看成男生,电影票看成女生,用兑换券 j 购买电影票 i 的花费是 -w(i, j)去建图,如果兑换券 j 无法购买电影票 i,那么花费 w(i, j)设置为负无穷大,去构造一个二分图尝试求解,我们会遇到一些问题:

改造一:如何满足 km 算法的使用条件?

因为 km 算法是用于求解二分图的最佳匹配,也就是说二分图必须存在最佳匹配才能使用 km 算法求解。存在最佳匹配的必要条件是:必须两边的点相同,而且至少存在一种匹配方案使得所有的点都被匹配。所以我们需要补点和补边(补点和补边也是使用 km 算法的常见的技巧)。补边策略:将不存在的边,权重设为-inf。补点策略:新增 x 张兑换券,第 y + i 张兑换券跟第 i 张电影票连边,权值为电影票的原价,这样一来可以保障把无穷大的结果排除在外,二来不需要再额外再计算使用原价购买的情况。

e785db90c5e76039318413811818b97f.png

改造二:如何在多个解中求出满足优先级的解?

我们可以把所有兑换券按照面值由小到大排序,如果面值一致,那么按照入账时间从早到晚排序,如果入驻时间一致,按照过期时间由早到晚排序,简而言之,把优先级高的券放在前面。枚举数量 k,对前 k 个兑换券和所有的电影票加入 km 模型中,计算出最少花费,只有花费变得比之前更小才更新答案。这样可以保证取得最少花费的同时,还能满足优先级。还有一个好处是,如果后续 pm 对策略的优先级进行调整,那么我们可以更改最初的排序规则即可。但是时间复杂度此时变成了 n^4。

如图所示,当 k 等于 2 的时候取得最优解,k=3 的时候有可能匹配到优先级低的券。

99fcb8f7c22d488f99080385ac6431de.png

改造三、时间复杂度的优化

经过改造一和改造二的处理后,算法时间复杂度是:(x+y)^4 + y*logy(x 代表电影票张数,y 代表兑换券张数)

时间复杂度分析:经过补点操作后,二分图两边的点都是 x + y 个。因为 km 算法的时间复杂度是 n ^ 3 次方,n 为二分图单侧的点的个数。所以当前的时间复杂度是 (x+y)^3 。为了处理匹配的优先级问题,我们对优惠券进行了优先级排序,时间复杂度是y*logy,在运行 km 算法的时候,枚举了数

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值