【算法】合唱团问题

 

题目:有 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

本题知识点:动态规划

       先分享一个动态规划入门教程 很特别的一个动态规划入门——woshioosm。里面通过例子将整个动态规划的要素以及解题思路描述的十分生动。

        总结来说,动态规划有几个重要的模块:

A. 最优子结构:子问题最优时,母问题通过优化选择后一定最优。

B. 子问题重叠:母问题与子问题本质上是同一个问题。

C. 边界:子问题在一定时候就不需要再细分

D. 子问题独立:母问题在对子问题进行选择时,被选择的子问题两两互不影响。

E. 做备忘录:可以把问题的解法放在一个变量中,当再次遇到时可直接从变量中获得答案。(循环

引用文章的小结:

       那么遇到问题如何用动态规划去解决呢?根据上面的分析我们可以按照下面的步骤去考虑:

       1、构造问题所对应的过程。
       2、思考过程的最后一个步骤,看看有哪些选择情况。
       3、找到最后一步的子问题,确保符合“子问题重叠”,把子问题中不相同的地方设置为参数。
       4、使得子问题符合“最优子结构”。
       5、找到边界,考虑边界的各种处理方式。
       6、确保满足“子问题独立”,一般而言,如果我们是在多个子问题中选择一个作为实施方案,而不会同时实施多个方案,那么子问题就是独立的。
       7、考虑如何做备忘录。
       8、分析所需时间是否满足要求。
       9、写出转移方程式。
      

回到本题:

        从最后步骤开始分析。首先为了简化问题,首先考虑能力值为正的情况。莫状态,在nn位置上的人为第k人(数组从0开始计数,为0,1..,k-1)。由此,可以用数组表示为 f[k-1][nn]。该值为第k-1个选择后的能力值 * 当前人的能力值:

                                                                       f[k-1][nn] = f[k-2][nn']*power[nn]

        这里的未知量为f[k-2][nn']即我们并不知道第k-1个数位置在哪,因而采用遍历的方法循环,约束条件为前d个数,找到最大的数记录下来:

for j in range(max(0,nn-d),nn):
    fm[kk][nn] = max(fm[kk][nn], fm[kk-1][j]*power[nn])

     这是最根本的子问题,接下来可以通过遍历k得到 当nn层为第1,2,....,k人时的最大值。最后遍历每一层,得到最终的最大值。

       这里有一个需要注意的问题,实际上能力值并不一定是正,因而正正得正,负负得正,若当前能力值为正,则选择前k-1个选择得到的最大值,但当前能力值为负,需要乘以前k-1个选择得到的最小值,才能最大。因而在上述基础上,新增一个数组。

#coding=utf-8
import copy
n = raw_input().strip()
power = raw_input().strip().split()
k,d = raw_input().strip().split()


# data processing
n = int(n)
power = [int(i) for i in power]
k = int(k)
d = int(d)


# 动态规划
fm = [[copy.deepcopy(0) for  _ in range(n)] for _ in range(k)]
fn = [[copy.deepcopy(0) for  _ in range(n)] for _ in range(k)]

val = 0
for nn in range(n):
    fm[0][nn] = power[nn]
    fn[0][nn] = power[nn]
    for kk in range(1,k):
        for j in range(max(0,nn-d),nn):
            fm[kk][nn] = max(fm[kk][nn],max(fm[kk-1][j]*power[nn],fn[kk-1][j]*power[nn]))
            fn[kk][nn] = min(fn[kk][nn],min(fm[kk-1][j]*power[nn],fn[kk-1][j]*power[nn]))

    val = max(val,fm[k-1][nn])
print val

小结:

        本题核心复习动态规划的概念及分析方法。属于常见题型,务必掌握。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章