动态规划学习
[编程题]合唱团
有 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
思路:
分析:该题目是一个动态规划的问题,那么我们首先要构造出状态转移方程。不妨设maxVal[i][j]表示以第i个人为最后一个(前面共i个人,最后一个人必选),一共选取了j个人(包含i)时的最大乘积。
同理,minVal[i][j]表示同样状态下的最小乘积(由于数据中存在负数,负数乘上某个极大的负数反而会变成正的极大值,因而需要同时记录最小值)。maxVal[i][j]很显然与maxVal[i][j-1]相关,可以理解为maxVal[i][j]由两部分组成,一部分是自身作为待选值,另一部分是maxVal[i][j-1]加上一个人后得到的值,然后取它们的极大值,由此可以得到状态转移方程如下:
maxVal[i][j] = max(maxVal[i][j], max(maxVal[c][j - 1] * a[i], minVal[c][j - 1] * a[i]));
minVal[i][j] = min(minVal[i][j], min(maxVal[c][j - 1] * a[i], minVal[c][j - 1] * a[i]));
最后遍历Maxval[i][k]即可得到最大值。
#include <iostream> #include <algorithm> using namespace std; //因为题目的取值范围最多50个人,最多选10个。所以数组长度分别取51,11 long long maxVal[51][11]; //存放当前选取第i个人(已经选了j个人)最大值记录。(第i个人必选) long long minVal[51][11]; //存放当前选取第i个人(已经选了j个人)最小值记录。(第i个人必选) int a[51]; //存放个人能力值 int main(void) { int N, K, D; long long result = 0; //返回输出结果 cin >> N; for (int i = 0; i < N; ++i) { cin >> a[i]; } cin >> K >> D; for (int i = 0; i < N; ++i) { maxVal[i][0] = minVal[i][0] = a[i]; } for (int i = 0; i < N; ++i) { for (int j = 1; j < K; ++j) { for (int c = i - 1; c >= max(i - D,0); --c) { maxVal[i][j] = max(maxVal[i][j], max(maxVal[c][j - 1] * a[i], minVal[c][j - 1] * a[i])); minVal[i][j] = min(minVal[i][j], min(maxVal[c][j - 1] * a[i], minVal[c][j - 1] * a[i])); } } result = max(result, max( maxVal[i][K - 1] ,minVal[i][K - 1]) ); } cout << result << endl; getchar(); return 0; }
动态规划学习总结:
思路:
把大问题转化成小问题,先满足当前条件,然后转移到下一个k,判断k是否要被选择,判断依据是是否比上一次更优解。然后依次转移,直到最后选择出最优解。
个人理解,还不深入,再我进行多练习之后再加总结。
2016-08-31 15:17:10