题目:
有 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
思路:采用动态规划
因为有正有负,负负得正,所以要维护两个dp数组,一个存储最大,一个存储最小。
定义fm[k][i]表示当选中了k个学生,并且以第i个学生为结尾,所产生的最大乘积;
fn[k][i]表示 当选中了k个学生,并且以第i个学生为结尾,所产生的最小乘积;
那么fm[k+1][i+1]=max(fm[k][i]*stu[i+1],fn[k][i]*stu[i+1]),
即当选中了k个学生后,再选择第i+1编号学生,所产生的最大乘积;
然而,并不能保证上一次选择的就是第i个学生,所以要遍历子数组fm[k],
令j从i到1,并且j与i+1之间小于间隔D,遍历fm[k][j],以及fn[k][j];
同理fn[k+1][i+1]=min(fn[k][i]*stu[i+1],fm[k][i]*stu[i+1])。
最后,遍历一遍fm[K][i]求得最大值(i从1~N)。
代码:
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <limits.h> 5 using namespace std; 6 7 int main() 8 { 9 int n, k, d; 10 cin >> n; 11 vector<int> ai(n+1); 12 int temp; 13 //下标从1开始 14 for (int i = 1; i <= n;++i) 15 { 16 cin >> temp; 17 ai[i] = temp; 18 } 19 cin >> k >> d; 20 21 vector<vector<long long>> maxRes(n + 1, vector<long long>(k + 1)); 22 vector<vector<long long>> minRes(n + 1, vector<long long>(k + 1)); 23 24 //max[i][j]表示已第i个学生结尾,包括该学生总共j个的结果 25 26 for (int i = 1; i <= n; ++i) 27 { 28 maxRes[i][1] = minRes[i][1] = ai[i]; 29 } 30 31 //序列元素原始位置,从1遍历到n 32 for (int iIndex = 1; iIndex <= n; ++iIndex) 33 { 34 //作为候选元素的位置 35 for (int kIndex = 1; kIndex <= k; ++kIndex) 36 { 37 for (int j = iIndex - 1; j > 0 && iIndex - j <= d;--j) 38 { 39 maxRes[iIndex][kIndex] = max(maxRes[iIndex][kIndex], 40 max(maxRes[j][kIndex - 1] * ai[iIndex], minRes[j][kIndex - 1] * ai[iIndex])); 41 minRes[iIndex][kIndex] = min(minRes[iIndex][kIndex], 42 min(maxRes[j][kIndex - 1] * ai[iIndex], minRes[j][kIndex - 1] * ai[iIndex])); 43 } 44 } 45 } 46 47 long long ans = LONG_MIN; 48 for (int i = 1; i <= n; ++i) 49 { 50 if (ans < maxRes[i][k]) 51 ans = maxRes[i][k]; 52 } 53 cout << ans; 54 }