目录
题目:
「云闪付」作为各方联手打造的全新移动端统一入口,致力成为消费者省钱省心的移动支付管家。
请你设计一个「云闪付」优惠活动管理系统 DiscountSystem
,具体功能如下:
-
AddActivity(int actId, int priceLimit, int discount, int number, int userLimit)
-- 表示创建编号为actId
的优惠减免活动:- 单笔消费的原价不小于
priceLimit
时,可享受discount
的减免 - 每个用户最多参与该优惠活动
userLimit
次 - 该优惠活动共有
number
数量的参加名额
- 单笔消费的原价不小于
-
RemoveActivity(actId)
-- 表示结束编号为actId
的优惠活动 -
Consume(int userId, int cost)
-- 表示用户userId
产生了一笔原价为cost
的消费。请返回用户的实际支付金额。- 单次消费最多可参加一份优惠活动
- 若可享受优惠减免,则 「支付金额 = 原价 - 优惠减免」
- 若同时满足多个优惠活动时,则优先参加优惠减免最大的活动
注:若有多个优惠减免最大的活动,优先参加
actId
最小的活动
注意:
actId
全局唯一
示例 1:
输入:
["DiscountSystem","addActivity","consume","removeActivity","consume"]
[[],[1,15,5,7,2],[101,16],[1],[102,19]]
输出:
[null,null,11,null,19]
解释:
DiscountSystem obj = DiscountSystem(); // 初始化系统
obj.addActicity(1,15,5,7,2); //创建编号1
的优惠活动,单笔消费原价不小于15
时,
可享受5
的减免,优惠活动共有7
个名额,每个用户最多参与该活动2
次
obj.consume(101,16); //用户101
消费了16
,满足优惠活动1
条件,返回实际支付16 - 5 = 11
obj.removeActivity(1); // 结束编号为1
的优惠活动
obj.consume(102,19); //用户101
消费了19
,目前不存在任何优惠活动,需按原价支付,返回19
示例 2:
输入:
["DiscountSystem","addActivity","addActivity","consume","removeActivity","consume","consume","consume","consume"]
[[],[1,10,6,3,2],[2,15,8,8,2],[101,13],[2],[101,17],[101,11],[102,16],[102,21]]
输出:
[null,null,null,7,null,11,11,10,21]
解释:
DiscountSystem obj = DiscountSystem(); // 初始化系统
obj.addActivity(1,10,6,3,2); // 创建编号1
的优惠活动,单笔消费原价不小于10
时,
可享受6
的减免,优惠活动共有3
个名额,每个用户最多参与该活动2
次
obj.addActivity(2,15,8,8,2); // 创建编号2
的优惠活动,单笔消费原价不小于15
时,
可享受8
的减免,优惠活动共有8
个名额,每个用户最多参与该活动2
次
obj.consume(101,13); // 用户101
消费了13
,仅满足优惠活动1
条件,返回实际支付13 - 6 = 7
用户101
参加1
次活动1
,活动1
剩余2
个名额
obj.consume(101,8); // 用户101
消费了8
,不满足任何活动,返回支付原价8
obj.removeActivity(2); // 结束编号为2
的活动
obj.consume(101,17); // 用户101
消费了17
,满足优惠活动1
条件,返回实际支付17 - 6 = 11
用户101
参加2
次活动1
,活动1
剩余1
个名额
obj.consume(101,11); // 用户101
消费了11
,满足优惠活动1
条件,但已达到参加次数限制,返回支付原价11
obj.consume(102,16); // 用户102
消费了16
,满足优惠活动1
条件,返回实际支付16 - 6 = 10
用户102
参加1
次活动1
,活动1
无剩余名额
obj.consume(102,21); // 用户102
消费了21
,满足优惠活动1
条件,但活动1
已无剩余名额,返回支付原价21
提示:
1 <= addActicity, removeActivity, consume 累计操作数 <= 10^3
0 <= actId, userId <= 1000
1 <= discount < priceLimit <= 10^5
1 <= cost <= 10^5
1 <= number <= 1000
1 <= userLimit <= 10
c++解法:
题意
哈希表
思路
- 题目本身只是考察设计与数据结构,实际的解法肯定许多种。我们设计一个数据结构来存储每个
Activity
的信息,分别存储:
priceLimit
: 允许折扣的价格int discount
: 折扣的金额number
: 当前还剩余多少个名额userLimit
: 每个用户的优惠限制次数record
: 记录每个用户已经使用的优惠的次数。
- 分别对每个
API
进行操作:
addActivity
: 生成一个新的Activity
即可。removeActivity
:从列表中将去删除即可。consume
:这部稍微复杂点,首先找到最大可能的优惠,满足的前提是当前priceLimit >= cost
,number > 0
,record[userid] < userLimit
,满足以上三个条件就记录下该Activity
的id
以及优惠。 找到最大的优惠后,对应Activity
信息更新,主要涉及到number
与record
的更新即可。
- 复杂度分析:
- 时间复杂度:
, addActivity
与removeActivity
均为 , consume
为,其中
- 为
Activity
的数量。 - 空间复杂度:
- ,
- 为
Activity
的数量。
代码
struct Activity { int priceLimit; int discount; int number; int userLimit; unordered_map<int, int> record; Activity(int priceLimit, int discount, int number, int userLimit){ this->priceLimit = priceLimit; this->discount = discount; this->number = number; this->userLimit = userLimit; } }; class DiscountSystem { private: map<int, Activity *> cnt; public: DiscountSystem() { } void addActivity(int actId, int priceLimit, int discount, int number, int userLimit) { cnt[actId] = new Activity(priceLimit, discount, number, userLimit); } void removeActivity(int actId) { cnt.erase(actId); } int consume(int userId, int cost) { int maxdiscount = 0; int id = -1; for(auto &[actId, pAct]: cnt) { if(cost >= pAct->priceLimit) { if(pAct->discount > maxdiscount && pAct->number > 0) { if(pAct->record.count(userId)){ if(pAct->record[userId] < pAct->userLimit){ maxdiscount = pAct->discount; id = actId; } }else{ maxdiscount = pAct->discount; id = actId; } } } } if(id >= 0){ cnt[id]->record[userId]++; cnt[id]->number--; } return cost - maxdiscount; } };
- 为
代码详解:
这段C++代码定义了两类,Activity
和 DiscountSystem
,并提供了与折扣活动和消费相关的功能。让我逐个部分详细解释:
struct Activity {
int priceLimit;
int discount;
int number;
int userLimit;
unordered_map<int, int> record;
Activity(int priceLimit, int discount, int number, int userLimit) {
this->priceLimit = priceLimit;
this->discount = discount;
this->number = number;
this->userLimit = userLimit;
}
};
-
Activity
结构体:这个结构体表示一个折扣活动。它包括了以下成员变量:priceLimit
:价格限制,用户的消费金额必须达到或超过此限制才能参与活动。discount
:折扣金额,表示用户在参与活动时可以获得的折扣金额。number
:剩余活动数量,表示这个活动还剩下多少个名额。userLimit
:用户参与次数限制,表示一个用户最多可以参与活动的次数。record
:使用无序映射(unordered_map)记录每个用户参与活动的次数。
-
Activity
结构体的构造函数:构造函数用于初始化Activity
对象的各个成员变量。在对象创建时,将传入的参数值分配给相应的成员变量。
class DiscountSystem {
private:
map<int, Activity *> cnt;
public:
DiscountSystem() {
}
void addActivity(int actId, int priceLimit, int discount, int number, int userLimit) {
cnt[actId] = new Activity(priceLimit, discount, number, userLimit);
}
void removeActivity(int actId) {
cnt.erase(actId);
}
int consume(int userId, int cost) {
int maxdiscount = 0;
int id = -1;
for (auto &[actId, pAct] : cnt) {
if (cost >= pAct->priceLimit) {
if (pAct->discount > maxdiscount && pAct->number > 0) {
if (pAct->record.count(userId)) {
if (pAct->record[userId] < pAct->userLimit) {
maxdiscount = pAct->discount;
id = actId;
}
} else {
maxdiscount = pAct->discount;
id = actId;
}
}
}
}
if (id >= 0) {
pAct->record[userId]++;
pAct->number--;
}
return cost - maxdiscount;
}
};
-
DiscountSystem
类:这个类用于管理折扣活动和消费的主要功能。它包括以下成员变量和方法:cnt
:使用map
存储不同折扣活动的信息,每个活动由一个唯一的actId
标识,并关联到相应的Activity
对象指针。
-
DiscountSystem
类的构造函数:构造函数为空,表示在创建DiscountSystem
对象时不需要额外的初始化操作。 -
addActivity
方法:这个方法用于添加新的折扣活动。它接受actId
以及与该活动相关的priceLimit
、discount
、number
和userLimit
。在内部,它创建一个新的Activity
对象,并将其与actId
关联,然后将该关联添加到cnt
中。 -
removeActivity
方法:这个方法用于移除指定的折扣活动。它接受一个actId
参数,然后在cnt
中查找并删除与该actId
相关的Activity
对象。 -
consume
方法:这是核心的消费方法。它接受userId
和cost
作为参数,用于模拟用户的消费操作并计算折扣。以下是详细解释:- 初始化
maxdiscount
和id
,分别用于跟踪用户能够获得的最大折扣金额和适用的活动标识。 - 使用
for
循环遍历cnt
中的活动,其中auto &[actId, pAct]
允许同时迭代活动的标识和对应的Activity
对象指针。 - 在迭代过程中,检查用户的消费是否达到当前活动的价格限制。
- 如果达到价格限制,进一步检查当前活动的折扣是否大于已记录的最大折扣,以及该活动的剩余数量是否大于0。
- 如果满足这些条件,再检查用户是否已经参加了该活动,以及是否已达到参与次数的限制。
- 如果找到适用的活动,更新
maxdiscount
和id
。 - 如果找到了适用的活动(
id >= 0
),则更新相应的Activity
对象的记录和剩余数量,并返回用户实际支付的金额(原始消费减去最大折扣)
- 初始化
python3解法:
算法描述:
代码建立了两个类:Activity和DiscountSystem,用于管理优惠活动和消费记录。
Activity 类:
这个类代表了一个优惠活动。它的构造函数 __init__ 接受四个参数:priceLimit(价格限制)、discount(折扣)、number(活动数量)、和 userLimit(用户限制)。
这些参数分别表示:活动的价格限制(只有当消费满足该限制时才能参加活动)、折扣金额、活动的剩余数量、和每个用户可参加活动的次数。
record 字典用于跟踪每个用户参加活动的次数。
DiscountSystem 类:
这个类用于管理多个不同的优惠活动。它的构造函数 __init__ 初始化一个空的 activities 字典,用于存储活动的信息。
addActivity 方法:
这个方法用于向 DiscountSystem 中添加新的优惠活动。
它接受 actId(活动的唯一标识符)以及与该活动相关的 priceLimit、discount、number 和 userLimit。
通过调用 Activity 的构造函数创建一个新的活动对象,并将其存储在 activities 字典中。
removeActivity 方法:
这个方法用于从 DiscountSystem 中删除特定的优惠活动,通过提供 actId 来指定要删除的活动。
consume 方法:
这个方法用于用户消费时查找适用于用户的最大折扣。
它接受 userId(用户标识符)和 cost(消费金额)作为参数。
方法遍历 activities 字典,查找用户满足价格限制的活动,然后比较这些活动的折扣和用户的历史参加次数,以确定用户可以享受的最大折扣。
如果找到适用的活动,它会更新 Activity 对象的 record 和 number 字段,并返回最终消费金额减去折扣金额。
总之,这个代码演示了一个优惠活动管理系统,用户可以通过 DiscountSystem 添加和删除不同的活动,然后在消费时根据条件享受相应的折扣。活动的参与次数和剩余数量也得到了跟踪。
时间复杂度分析:
这个系统的时间复杂度主要受到两个因素的影响:
添加活动 (addActivity):这是一个O(1)的操作,因为它只涉及将新活动添加到字典中。
用户消费 (consume):在最坏情况下,需要遍历所有活动,所以它的时间复杂度是O(n),其中n是活动的数量。
代码:
class Activity:
def __init__(self, priceLimit, discount, number, userLimit):
self.priceLimit = priceLimit
self.discount = discount
self.number = number
self.userLimit = userLimit
self.record = {}
class DiscountSystem:
def __init__(self):
self.activities = {}
def addActivity(self, actId, priceLimit, discount, number, userLimit):
self.activities[actId] = Activity(priceLimit, discount, number, userLimit)
def removeActivity(self, actId):
del self.activities[actId]
def consume(self, userId, cost):
maxdiscount = 0
id = -1
for actId, pAct in self.activities.items():
if cost >= pAct.priceLimit:
if pAct.discount > maxdiscount and pAct.number > 0:
if userId in pAct.record:
if pAct.record[userId] < pAct.userLimit:
maxdiscount = pAct.discount
id = actId
else:
maxdiscount = pAct.discount
id = actId
if id >= 0:
self.activities[id].record[userId] = self.activities[id].record.get(userId, 0) + 1
self.activities[id].number -= 1
return cost - maxdiscount
代码详解:
class Activity:
def __init__(self, priceLimit, discount, number, userLimit):
self.priceLimit = priceLimit
self.discount = discount
self.number = number
self.userLimit = userLimit
self.record = {}
这部分定义了 Activity
类。这个类有一个构造函数 __init__
,它接受四个参数:priceLimit
、discount
、number
和 userLimit
。这些参数代表了活动的价格限制、折扣、剩余数量和用户限制。同时,创建了一个空字典 record
用于跟踪用户参与活动的次数。
class DiscountSystem:
def __init__(self):
self.activities = {}
在这里,定义了 DiscountSystem
类,它包含一个构造函数 __init__
,该构造函数初始化一个空的字典 activities
,用于存储不同活动的信息。
def addActivity(self, actId, priceLimit, discount, number, userLimit):
self.activities[actId] = Activity(priceLimit, discount, number, userLimit)
这是 DiscountSystem
类的一个方法,addActivity
。它接受 actId
(活动的唯一标识符)以及与该活动相关的 priceLimit
、discount
、number
和 userLimit
。方法的作用是将一个新的 Activity
对象添加到 activities
字典中,使用 actId
作为键。
def removeActivity(self, actId):
del self.activities[actId]
这是 DiscountSystem
类的另一个方法,removeActivity
。它接受 actId
作为参数,用于从 activities
字典中删除指定的活动。
def consume(self, userId, cost):
maxdiscount = 0
id = -1
for actId, pAct in self.activities.items():
if cost >= pAct.priceLimit:
if pAct.discount > maxdiscount and pAct.number > 0:
if userId in pAct.record:
if pAct.record[userId] < pAct.userLimit:
maxdiscount = pAct.discount
id = actId
else:
maxdiscount = pAct.discount
id = actId
if id >= 0:
self.activities[id].record[userId] = self.activities[id].record.get(userId, 0) + 1
self.activities[id].number -= 1
return cost - maxdiscount
-
maxdiscount
和id
的初始化:maxdiscount
用于跟踪用户可以享受的最大折扣金额。初始值设为0。id
用于记录适用于用户的活动的标识符。初始值设为-1,表示未找到适用的活动。
-
迭代活动:
for actId, pAct in self.activities.items():
这一行开始迭代activities
字典中的每个活动。actId
是活动的唯一标识符,pAct
是与该标识符相关联的Activity
对象。
-
检查价格限制:
if cost >= pAct.priceLimit:
这一行检查用户的消费金额是否达到了当前活动的价格限制。只有当消费满足价格限制时,才会考虑这个活动。
-
检查折扣和剩余数量:
if pAct.discount > maxdiscount and pAct.number > 0:
这一行检查当前活动的折扣是否大于已经记录的最大折扣(maxdiscount
),并且该活动的剩余数量(number
)是否大于0。- 如果这个活动满足条件,将进一步检查用户是否符合参加活动的条件。
-
检查用户记录:
if userId in pAct.record:
这一行检查用户是否在当前活动的记录中。如果用户之前参加过该活动,就需要检查用户是否达到了参加活动的次数限制。if pAct.record[userId] < pAct.userLimit:
如果用户的参与次数未达到限制,将更新maxdiscount
为该活动的折扣金额,并将id
设置为当前活动的标识符。
-
用户未在记录中的情况:
- 如果用户不在当前活动的记录中,也会将
maxdiscount
设置为该活动的折扣金额,并将id
设置为当前活动的标识符。
- 如果用户不在当前活动的记录中,也会将
-
应用折扣和更新记录:
- 如果
id
大于等于0,表示找到了适用的活动,将会执行以下步骤:- 更新
Activity
对象的record
字典,将用户标识符userId
对应的值加1,表示用户参加了该活动。 - 减少活动的剩余数量
number
,表示一个用户参加了该活动,活动的可用数量减少了一个。
- 更新
- 最后,方法返回用户的消费金额减去最大折扣金额,即用户实际支付的金额。
- 如果
这个方法的主要目的是在用户消费时,查找适用于用户的最大折扣活动,然后更新活动记录和剩余数量,最后计算用户实际支付的金额。这使得系统能够管理不同的优惠活动,确保用户获得适用的折扣。