动态规划_合唱团

没有涉及过动态规划相关的编程,编程时遇到了网易的合唱团问题,由此学习了一下。

基本概念:动态规划,是求解决策过程最优化的数学方法。

基本思想:解决不同子问题,合并子问题的解得出原问题的解。由于动态规划解决的问题多数有重叠子问题。为减少重复计算,对每个子问题只解一次,将其不同状态保存在一个二维数组中。

与分治法的相似:将求解问题分成子问题,先求子问题,后合并求原问题的解。

与分治法的不同:动态规划分解后的子问题不是相互独立的。

适用情况

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))

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值