题目
关键词
动态规划,滚动数组
思路
满足以下3个条件考虑用动态规划:
- 最优子结构:原问题的最优解可通过子问题的最优解获得;
- 无后效性:子问题的解一旦确定不会收到后续决策的影响;
- 子问题重叠:不同的子问题具有共同的子问题,每个子问题都是独立的;
显然该题满足以上条件。
动态规划的求解通常分为以下步骤:
- 将原问题划分为若干 阶段,每个阶段对应若干个子问题,提取这些子问题的特征(称之为 状态);
- 寻找每一个状态的可能 决策,或者说是各状态间的相互转移方式(用数学的语言描述就是 状态转移方程)。
- 按顺序求解每一个阶段的问题。
解:
记dp1[i]为前i项和为奇数的可能数,dp2[i]为前i项和为偶数的可能性。c1为当前项奇数个数,c2为当前项偶数个数。
则状态转移方程:
代码
def solution(numbers):
# Please write your code here
dp1 = [0] * len(numbers) # 前n项各取一个数和为奇数可能数
dp2 = [0] * len(numbers) # 前n项各取一个数和为偶数的可能数
for i, element in enumerate(numbers):
c1 = 0 # 当前数字中奇数个数
c2 = 0 # 当前数字中偶数个数
while element > 0:
cur = element % 10
if cur % 2 == 0:
c2 += 1
else:
c1 += 1
element //= 10
if (i == 0):
dp1[0] = c1
dp2[0] = c2
else:
dp1[i] = dp1[i - 1] * c2 + dp2[i - 1] * c1
dp2[i] = dp2[i - 1] * c2 + dp1[i - 1] * c1
return dp2[len(dp2) - 1]
if __name__ == "__main__":
# You can add more test cases here
print(solution([123, 456, 789]) == 14)
print(solution([123456789]) == 4)
print(solution([14329, 7568]) == 10)
复杂度
时间复杂度: O(n * m),其中 n 是 numbers 列表的长度,m 是每个数字字符串的平均长度。
空间复杂度: O(n) 使用了两个长度为 n 的数组 dp1 和 dp2。
优化:利用滚动数组优化空间复杂度
观察到,dp1[i] 和 dp2[i] 的值仅依赖于前一项(dp1[i-1] 和 dp2[i-1])。因此,实际上我们并不需要存储所有的 dp 状态,而只需要保持上一项的状态即可。因此,我们可以将空间复杂度从 O(n) 降低到 O(1),只使用常量空间来保存前一项的状态。
代码
def solution(numbers):
# Please write your code here
dp1 = 0 # 前n项各取一个数和为奇数可能数
dp2 = 0 # 前n项各取一个数和为偶数的可能数
for i, element in enumerate(numbers):
c1 = 0 # 当前数字中奇数个数
c2 = 0 # 当前数字中偶数个数
while element > 0:
cur = element % 10
if cur % 2 == 0:
c2 += 1
else:
c1 += 1
element //= 10
if (i == 0):
dp1 = c1
dp2 = c2
else:
temDp1 = dp1 * c2 + dp2 * c1
temDp2 = dp2 * c2 + dp1 * c1
dp1 = temDp1
dp2 = temDp2
return dp2
if __name__ == "__main__":
# You can add more test cases here
print(solution([123, 456, 789]) == 14)
print(solution([123456789]) == 4)
print(solution([14329, 7568]) == 10)
总结
python中a // b(地板除)即js中的Math.floor(a / b),//=(地板除赋值);pyhon中没有++(自增操作符) 和--(自减操作符);python中, 通过索引赋值时要注意,如果索引不在数组范围内会报IndexError错误,因此往往需要提前声明好数组长度,或者使用append方法;
arr = []
arr[0] = 1 # IndexError: list assignment index out of range
- 滚动数组技术是一种在动态规划中优化空间复杂度的常用技巧。通过只保留当前状态和前一状态,可以有效地将空间复杂度从
O(n)降低到O(1)。这种技术常用于解决“前一项”依赖的动态规划问题,如斐波那契数列、最大子数组和、背包问题等。