没有涉及过动态规划相关的编程,编程时遇到了网易的合唱团问题,由此学习了一下。
基本概念:动态规划,是求解决策过程最优化的数学方法。
基本思想:解决不同子问题,合并子问题的解得出原问题的解。由于动态规划解决的问题多数有重叠子问题。为减少重复计算,对每个子问题只解一次,将其不同状态保存在一个二维数组中。
与分治法的相似:将求解问题分成子问题,先求子问题,后合并求原问题的解。
与分治法的不同:动态规划分解后的子问题不是相互独立的。
适用情况:
1,最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
2,无后效性:某阶段状态一旦确定,就不受这个状态以后决策的影响。
3,有重叠子问题:子问题之间是不独立的,一个子问题在下一阶段的决策中可能多次使用到。(此性质非动态规划适用的必要条件,但如果没有这个性质,动态规划与其他算法相比就没有优势)
本质:多阶段决策问题
求解的基本步骤:
初始状态——>——>对中间阶段决策的选择——>——>结束状态
初始状态——>决策1——>决策2——>………————>结束状态
1,划分阶段
2,确定状态及状态变量
3,确定决策并写出状态转移方程
4,寻找边界条件
网易合唱团
有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
输入描述:
每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
输出描述:
输出一行表示最大的乘积。
输入例子:
3
7 4 7
2 50
输出例子:
49
问题分解:
求第j个元素与之前的最大乘积相乘的结果。(因能量值可以为负,也需计算第j个元素与之前的最小乘积相乘的结果)
对于第j个元素及之前元素的相对位置,用d确定
总循环条件为取够k个元素
结构分析:
根据分解的结果,可以确定,需要三重循环来完成题目。
最外层:for i in range(1,k) 循环K次,取出k名学生。
中间层:for j in range(i,n) 遍历i到n,假定j为第k个元素所在的位置。
最内层:for x in range(j-d,j) 获取j为最后一个元素时,满足位置不超过d的子序列的乘积。
存储结构:
用列表dp存储对应位置的最大乘积和最小乘积。比如dp[i]表示以第i个元素为结尾的最大乘积和最小乘积(max,min)
代码展示:
def choir(n,a,k,d):
dp=[(e,e) for e in a] #初始值为二维数组,每个元素为(a[i],a[i])
for i in range(1,k):
dpp=dp[:i] #取dp的前i个元素
for j in range(i,n):
temp_list=[] #定义临时数组,用于接收a[j]与子序列的乘积
for x in range(j-d,j):
# print(x)
if x<0:
continue
else:
temp_list.append(a[j]*dp[x][0])
temp_list.append(a[j]*dp[x][1])
dpp.append((max(temp_list),min(temp_list)))
dp=dpp
print(dp)
return max([max(e) for e in dp])
if __name__=='__main__':
n=int(input())
a=[int(i) for i in input().split()]
k,d=[int(i) for i in input().split()]
print(choir(n,a,k,d))