11月14日 构造特定数组的逆序拼接
问题描述
小U得到了一个数字n,他的任务是构造一个特定数组。这个数组的构造规则是:对于每个i从1到n,将数字n到i逆序拼接,直到i等于n为止。最终,输出这个拼接后的数组。
例如,当n等于3时,拼接后的数组是 [3, 2, 1, 3, 2, 3]。
思考
可以构造一个特定的数组,通过从 1 到 n 的循环,对每个 i 构造从 n 到 i 的逆序部分数组,并拼接到最终结果中。
代码
def solution(n: int) -> list:
result = []
for i in range(1, n+1, 1):
result.extend(range(n, i-1, -1))
return result
知识点:
- range 的逆序用法:range(n, i-1, -1) 会生成从 n 到 i(含 i)的数字序列,步长为 -1。
- list.extend:将一个可迭代对象中的元素逐一添加到列表尾部。
感想
本题比较简单,我们通过使用 range 的逆序生成特性,避免了手动构造和翻转数组,简洁高效。
11月15日 饭馆菜品选择问题
问题描述
小C来到了一家饭馆,这里共有 n 道菜,第 i 道菜的价格为 a_i。其中一些菜中含有蘑菇,s_i 代表第 i 道菜是否含有蘑菇。如果 s_i = '1',那么第 i 道菜含有蘑菇,否则没有。 小C希望点 k 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有 m 道菜含有蘑菇。小C想知道在满足条件的情况下能选出的最小总价格是多少。如果无法按照要求选择菜品,则输出-1。
思考
问题分析: 小C需要在满足 菜品数量 = k 和 蘑菇菜数量 ≤ m 的条件下,找到最小的总价格。 由于蘑菇菜和非蘑菇菜有不同的限制条件,这需要进行分类处理,分别对两类菜进行筛选。 动态规划是解决这个问题的核心工具,适用于这类约束优化问题。
实现步骤:
- 分类处理: 将所有菜品分为 蘑菇菜 和 非蘑菇菜。 分别对两类菜按照价格升序排序。
- 动态规划建模: 使用二维数组dp[i][j] ,表示选择了 i 道菜,其中有 j 道是蘑菇菜时的最小总价格。 dp[0][0] = 0 表示没有选菜时价格为 0。
- 动态转移: 遍历非蘑菇菜,将它们加入到 dp 中更新价格。 遍历蘑菇菜,限制蘑菇菜数量 j 的上限。
- 结果提取: 通过检查 dp[k][j] 中满足 j≤m 的最小值来得到答案。
代码
def solution(s: str, a: list, m: int, k: int) -> int: # 分类菜品为含有蘑菇的和不含蘑菇的 mushroom_dishes = [] no_mushroom_dishes = [] # 分类操作 for i in range(len(s)): if s[i] == '1': mushroom_dishes.append(a[i]) else: no_mushroom_dishes.append(a[i]) # 对菜品进行价格排序,选择最便宜的 mushroom_dishes.sort() no_mushroom_dishes.sort() # 菜品总数 n_mushroom = len(mushroom_dishes) n_no_mushroom = len(no_mushroom_dishes) # 如果无法选择k道菜,直接返回-1 if n_mushroom + n_no_mushroom < k: return -1 # 动态规划数组,dp[i][j]表示选择i道菜并且含有j道蘑菇的最小价格 dp = [[float('inf')] * (m + 1) for _ in range(k + 1)] # 初始条件 dp[0][0] = 0 # 选择0道菜时,价格是0 # 处理不含蘑菇的菜 for price in no_mushroom_dishes: for i in range(k, 0, -1): # 从后往前,防止覆盖之前的结果 for j in range(m + 1): dp[i][j] = min(dp[i][j], dp[i-1][j] + price) # 处理含蘑菇的菜 for price in mushroom_dishes: for i in range(k, 0, -1): for j in range(1, m + 1): # j必须大于0,表示要加蘑菇 dp[i][j] = min(dp[i][j], dp[i-1][j-1] + price) # 找到选择k道菜且蘑菇数量不超过m的最小总价 result = min(dp[k][j] for j in range(m + 1)) return result if result != float('inf') else -1
这道题展示了如何在有限资源和多约束条件下,利用动态规划找到最优解,既是实践算法的良好案例,也是培养优化思维的好机会。这道题虽然解决了特定问题,但方法具有很强的通用性。比如,在解决商品推荐、旅行规划等问题时,可以将动态规划与贪心思想结合。更复杂的场景(如多类菜品的分配)也可以借鉴此方法。
11月16日 比赛配对问题
问题描述
小R正在组织一个比赛,比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制: 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,且产生 n / 2 支队伍进入下一轮。
如果当前队伍数为 奇数,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行 (n - 1) / 2 场比赛,且产生 (n - 1) / 2 + 1 支队伍进入下一轮。 小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。
思考
这个问题是模拟一种淘汰赛制,直到剩下唯一的胜利者。每一轮比赛会减少队伍数量,关键在于计算总共进行了多少场比赛。 每场比赛会淘汰一支队伍,因此在总共 n 支队伍中,要决出唯一的胜利者,需要淘汰 n−1 支队伍。因此,比赛的总场次就是 n−1。
代码
def solution(n: int) -> int: return n-1
本题非常简单,直接通过数学归纳发现淘汰赛的总场次数与队伍数直接相关,问题的复杂性被巧妙简化。 提示我们在处理递归或模拟问题时,可以尝试通过数学规律优化。
11月17日 小F的永久代币卡回本计划
问题描述
小F最近迷上了玩一款游戏,她面前有一个永久代币卡的购买机会。该卡片的价格为 a 勾玉,每天登录游戏可以返还 b 勾玉。小F想知道她至少需要登录多少天,才能让购买的永久代币卡回本。
思考
本题的本质内容是每天返还 b 勾玉的累计收益,何时达到或超过 a 勾玉的购买成本。 可以推导: 小F每天获得 b 勾玉,想知道需要多少天才能回本,即求最小整数 d,满足:d×b≥a
变形得:
可以直接使用整除运算和上取整公式。
代码
def solution(a: int, b: int) -> int: return (a + b - 1) // b
今天打卡的这道题和昨天的一样,都是直观易懂,通过简单公式快速解答,强调了解决问题时的逻辑推导能力。通过发现问题的本质规律,可以大幅提升解决效率,可以避免繁琐的模拟或递归过程。