奇怪的打印机 III(颜色扩展与成本优化)
字数 1423 2025-12-03 19:43:32
奇怪的打印机 III(颜色扩展与成本优化)
题目描述
有一台奇怪的打印机,它每次打印可以连续打印一段任意长度的相同字符,但每次打印都有固定的启动成本。打印机的特殊之处在于:
- 打印机有无限次打印机会,但每次打印都有启动成本C
- 每次打印可以选择任意起始位置i和结束位置j,将区间[i, j]内的所有字符打印成同一种颜色
- 如果目标字符串中某个位置已经有颜色,新的打印会覆盖之前的颜色
- 打印机的墨水成本与打印长度无关,只与启动次数有关
给定一个目标字符串s,求打印出该字符串的最小总成本。
解题过程
步骤1:问题分析
这是一个典型的区间动态规划问题。我们需要找到最优的打印顺序来最小化总成本。关键观察是:
- 如果目标字符串为空,成本为0
- 如果目标字符串所有字符相同,只需要1次打印,成本为C
- 对于一般情况,我们需要考虑如何分割区间和合并操作
步骤2:定义状态
定义dp[i][j]表示打印出子串s[i...j]的最小成本。
步骤3:状态转移分析
考虑子串s[i...j]的打印方式:
-
基础情况:当i = j时,只需要1次打印,dp[i][j] = C
-
一般情况:考虑不同的打印策略
-
策略1:单独打印第一个字符,然后打印剩余部分
dp[i][j] = C + dp[i+1][j] -
策略2:如果s[i] = s[k](i < k ≤ j),可以先打印区间[i, k]为颜色s[i],这样打印区间[i+1, k-1]时可能可以节省成本
dp[i][j] = min(dp[i][j], dp[i+1][k-1] + dp[k][j])
-
步骤4:完善状态转移方程
更完整的状态转移方程:
- dp[i][j] = C + dp[i+1][j] (单独打印第一个字符)
- 对于所有k从i+1到j,如果s[i] == s[k]:
dp[i][j] = min(dp[i][j], dp[i+1][k-1] + dp[k][j]) - 如果s[i] == s[j]且j > i:
dp[i][j] = min(dp[i][j], dp[i+1][j]) (最后一个字符与第一个相同)
步骤5:边界条件处理
- 当i > j时,dp[i][j] = 0(空字符串)
- 当i = j时,dp[i][j] = C(单个字符)
步骤6:计算顺序
由于需要计算较大区间时依赖较小区间的结果,我们需要按区间长度从小到大计算:
- 长度为1的区间
- 长度为2的区间
- 长度为3的区间
- ...
- 长度为n的区间
步骤7:算法实现
def strange_printer_iii(s, C):
n = len(s)
if n == 0:
return 0
dp = [[float('inf')] * n for _ in range(n)]
# 初始化长度为1的区间
for i in range(n):
dp[i][i] = C
# 按区间长度从小到大计算
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
# 策略1:单独打印第一个字符
dp[i][j] = C + dp[i+1][j]
# 策略2:寻找与s[i]相同的字符进行合并打印
for k in range(i+1, j+1):
if s[i] == s[k]:
# 处理中间部分和右边部分
mid_cost = dp[i+1][k-1] if i+1 <= k-1 else 0
right_cost = dp[k][j] if k <= j else 0
dp[i][j] = min(dp[i][j], mid_cost + right_cost)
return dp[0][n-1]
步骤8:优化分析
上述算法的时间复杂度是O(n³),空间复杂度是O(n²)。我们可以进一步优化:
- 预处理优化:预处理每个位置后面第一个相同字符的位置,减少内层循环次数
- 记忆化搜索:使用记忆化递归可能在某些情况下更直观
步骤9:示例验证
例1:s = "abc",C = 1
- 每个字符都需要单独打印:成本 = 3
例2:s = "aba",C = 1
- 可以先打印整个区间为'a'(成本1),然后打印中间的'b'(成本1)
- 总成本 = 2
例3:s = "aabaa",C = 1
- 最优策略:先打印整个区间为'a',然后打印中间的'b'
- 总成本 = 2
这个算法能够有效处理各种复杂的字符模式,找到最优的打印顺序来最小化总成本。