动态规划之求连续子序列最大值,最大和等问题

4,135 阅读4分钟

最近笔试做到两道涉及到求给定序列满足某某条件连续子序列的题目,在此总结一下,一道是声网的笔试题,一道是网易互联网笔试的第三道编程题。

网易互联网

1.题目

给定一个长度为n的数字序列,对于每个1<=k<=n,求解出所有长度为k的连续子序列的最大值中的最小值。
输入:
第一行数字n代表数字序列长度
第二行为n个序列元素
输出: 当k分别为1-n时,长度为k的连续子序列的最大值的最小值
例如,输入:
6
1 3 2 4 6 5
输出:1 3 3 4 6 6

2.思路

可以定义一个dp二维矩阵,规模为M*N,其中M,N均等于序列num的长度n,dp[i][j]代表从序号i到序号j的连续子序列中的最大值。初始值为0。整个程序可以分成两步:step1:填充dp矩阵,step2:查询dp矩阵。

(1)填充dp矩阵

初始值用memset设为0

当i==j 时,dp[i][j]==num[i]

其余情况:dp[i][j]=max(dp[i][j-1],dp[j][j])

以例子中的输入为例,可得dp矩阵如下图:

0 1 2 3 4 5
0 1 3 3 4 6 6
1 3 3 4 6 6
2 2 4 6 6
3 4 6 6
4 6 6
5 5

(2)查询dp矩阵

将长度为k的连续子序列的最大值入vector max中

对max sort一下,取最小值即为长度为k的连续子序列最大值的最小值

3.代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define M 100
#define N 100
int dp[M][N];

void fullTheDP(vector <int> num)
{
	int len = num.size();
	//对角线上的元素为num中每个数本身的值
	for (int i = 0; i < len; i++)
		dp[i][i] = num[i];
	//只需填充dp矩阵的右上角即可,dp[i][j]表示从num中从序号i到序号j的子序列的最大值
	for (int i = 0; i < len - 1; i++)
		for (int j = i + 1; j < len; j++)
			dp[i][j] = max(dp[i][j - 1], dp[j][j]);
	return;
}

void findTheMinOfMaxOfK(int k, int len)
{
	vector <int> max;
	for (int i = 0; i < len - k + 1; i++)
	{
		max.push_back(dp[i][i + k - 1]);
	}
	sort(max.begin(), max.end());
	cout << max[0] << ' ';
}

int main()
{
	//处理输入
	int n;
	cin >> n;
	int i = n;
	vector <int> num;
	while (i--)
	{
		int tmp;
		cin >> tmp;
		num.push_back(tmp);
	}
	//填充dp矩阵
	memset(dp, 0, sizeof(dp));
	fullTheDP(num);
	//查询并输出
	int k = 1;
	while (k <= n)
	{
		findTheMinOfMaxOfK(k, n);
		k++;
	}
	cout << endl;
	return 0;
}

声网

1.题目

给定一个数字序列A1,...,An,求i,j,使得Ai+...+Aj最大,并输出这个最大和。
样例:
输入:
6
-2,11,-4,13,-5,-2
输出:
20

2.思路

可以定义一个dp二维矩阵,规模为M*N,其中M,N均等于序列num的长度n,dp[i][j]代表从序号i到序号j的连续子序列的和。初始值为0。整个程序可以分成两步:step1:填充dp矩阵,并用整数max存最大值以及ibegin,iend存最大和序列的开始终止序号;step2:输出,max为最大和,ibegin和iend为和为最大值的子序列的开始/结束下标。

(1)填充dp矩阵

初始值用memset设为0

当i==j 时,dp[i][j]==num[i]

其余情况:dp[i][j]=dp[i][j-1]+num[j]

(2)输出

3.代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define M 100
#define N 100
int dp[M][N];

void findTheMaxSumSeq(vector <int> num)
{
	//填充dp
	int len = num.size();
	int maxNum =num[0];
	int ibegin = 0, iend = 0;

	//只需填充dp矩阵的右上角即可,对角线上的元素为num中每个数本身的值,dp[i][j]表示从num中从序号i到序号j的子序列的最大值
	for (int i = 0; i < len ; i++)
		for (int j = i ; j < len; j++)
		{
			if (i == j)
			{
				dp[i][j] = num[i];
				if (dp[i][j] > maxNum)
				{
					maxNum = dp[i][j];
					ibegin = i;
					iend = j;
				}
			}
			else
			{
				dp[i][j] = dp[i][j - 1] + num[j];
				if (dp[i][j] > maxNum)
				{
					maxNum = dp[i][j];
					ibegin = i;
					iend = j;
				}
			}
		}

	cout << maxNum << endl;//输出最大和
	for (int i = ibegin; i <= iend; i++)//输出最大和对应的子序列
		cout << num[i] << ' ';
	cout << endl;
}


int main()
{
	//处理输入
	int n;
	cin >> n;
	int i = n;
	vector <int> num;
	while (i--)
	{
		int tmp;
		cin >> tmp;
		num.push_back(tmp);
	}
	//查找最大和子序列
	findTheMaxSumSeq(num);
	return 0;
}

没有测试很多用例,若有考虑不周的地方,望指出~