牛客网-合唱团dp动态规划

题目描述

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。

示例1

输入

3
7 4 7
2 50

输出

49

思路

设dpMax = [[1 for i in range(k+1)] for j in range(n+1)]
表示以第i个人为最后一个人,一共选取了j个人时的最大乘积。
同理,dpMin = [[1 for i in range(k+1)] for j in range(n+1)]
表示同样状态下的最小乘积(由于数据中存在负数,负数乘上某个极大的负数反而会变成正的极大值,因而需要同时记录最小值)。
dpMax[i][j] 很显然与dpMax[p][j-1] (for p in range(max(i-d, 0), i)表示遍历当前数之前符合范围条件的所有数, 而 j-1则表示所有数中每一个数对应的只比当前乘积因子数少一个时的结果)相关,可以理解为dpMax[i][j]由两部分组成,一部分是自身作为待选值,另一部分是dpMax[p][j-1]乘上一个人后得到的值,然后取它们的极大值,由此可以得到状态转移方程如下:
dpMax[i][j] = max(dpMax[i][j], max(dpMax[p][j-1]array[i-1], dpMin[p][j-1]array[i-1]))
dpMin[i][j] = min(dpMin[i][j], min(dpMax[p][j-1]array[i-1], dpMin[p][j-1]array[i-1]))

Python代码

n = int(input())
array = [int(x) for x in input().split()]
k, d =[int(x) for x in input().split()]

dpMax = [[1 for i in range(k+1)] for j in range(n+1)]
dpMin = [[1 for i in range(k+1)] for j in range(n+1)]
result = 0
for i in range(1, n+1):
    for j in range(1, k+1):
        for p in range(max(i-d, 0), i):
            dpMax[i][j] = max(dpMax[i][j], max(dpMax[p][j-1]*array[i-1], dpMin[p][j-1]*array[i-1]))
            dpMin[i][j] = min(dpMin[i][j], min(dpMax[p][j-1]*array[i-1], dpMin[p][j-1]*array[i-1]))
            if dpMax[i][j] > result:
                result = dpMax[i][j]
print(result)

题目来源

https://www.nowcoder.com/practice/661c49118ca241909add3a11c96408c8?tpId=85&tqId=29830&tPage=1&rp=1&ru=%2Fta%2F2017test&qru=%2Fta%2F2017test%2Fquestion-ranking

你可能感兴趣的:(牛客网-合唱团dp动态规划)