动态规划学习

[编程题]合唱团

有 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