动态规划:合唱团问题解析(二)

牛客网网易的校招编程题

题目:有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
输入:每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
输出:输出一行表示最大的乘积。

求解合唱团问题

在讨论了简化的问题之后,现在我们回过头来再看原问题,我们可以发现原问题就是在简化的问题上加了两个限制条件,一个是在这 n 个数中可以取到负值,另一个是取到的这 n 个数的索引值之间的差不能超过一个限定值 d。

对于第一个限制条件,我们必须考虑到当偶数个负值相乘时也可能产生最大的乘积,而能产生最大乘积的负数必然是其中最小的负数。所以我们可以以同样地动规方法开辟另一个空间去存储计算出的最小负值,然后在我们求最大值得时候,当选择了一个固定的值 n,我们必须从其与存储的最大值的积 (n*dpmax) 和存储的最小值的积 (n*dpmin) 中选出最大的值。同样地,我们必须更新存储最小负值的矩阵,当选择了一个固定的值 n,我们必须从其与存储的最大值的积和存储的最小值的积中选出最小的值。

对于第二个限制条件,只要修改向前搜索的最大值的范围就可以了,但要注意的是当这个范围越界的时候我们要做相应的处理。最终实现的代码如下所示:

 1 long long singProblem(int a[], int n, int k, int d) {
 2     vector<long long> dpmax = vector<long long>(n, 0);
 3     vector<long long> dpmin = vector<long long>(n, 0);
 4     long long result = 0;
 5     for (int j = 1; j < k + 1; j++) {
 6         for (int i = 0; i < n; i++) {
 7             if (j == 1) {
 8                 dpmax[i] = a[i];
 9                 dpmin[i] = a[i];
10             }
11             else {
12                 for (int index = i + 1; index <= min(i + d, n - 1); index++) {
13                     dpmax[i] = max(dpmax[i], max(a[i] * dpmax[index], a[i] * dpmin[index]));
14                     dpmin[i] = min(dpmin[i], min(a[i] * dpmax[index], a[i] * dpmin[index]));
15                 }
16             }
17             if (j == k) result = max(result, dpmax[i]);
18         }
19     }
20     return result;
21 }
posted @ 2018-03-08 23:04  ToBeDeveloper  阅读(988)  评论(0编辑  收藏  举报