HDU.1003 Max Sum

原题

HDU.1003 Max Sum

分类

动态规划

题意

计算从一个序列中最大连续子序列和、对应的起始元素和终止元素的位置。

输入/输出 要求与格式
样例数的确定 最开始一行开始输入样例数
每个样例的输入 每行n + 1个数,第一个数n为数组元素个数,后n个数为序列的实际元素
输出结果 最大连续子序列和、对应的起始元素和终止元素的位置
输出格式 每个案例结果第一行为"Case X:",第二行为3个输出结果(空格隔开),两个案例结果间一个空行
特殊要求 有多个解的案例使用最前面的解

以下是数据范围:

数据 数据范围
样例数 1 T 20 1 \leq T \leq 20
序列元素个数 1 n 1 0 5 1 \leq n \leq 10^5
序列元素 1 0 3 a i 1 0 3 -10^3 \leq a_i \leq 10^3

题解

这道题规定了数组元素个数最大可能有 1 0 5 10^5 个。

如果出于最暴力的方法来考虑,尝试每一种子序列的和,用前缀和的方法来优化求和,时间复杂度为 O ( n 2 ) O \left( n^2 \right) ,这样的计算方法肯定会TLE啊,必须换个思路。

其实在学动态规划的时候,我们老师讲的第一道例题就是这道,以下是动态规划的思路表格:

\ 具体内容
d p [ x ] dp \left[ x \right] 序列前 x x 个数中,必须包含 a [ x ] a \left[ x \right] 的最大连续序列和
初始状态 d p [ 1 ] = a [ 1 ] dp \left[ 1 \right] = a \left[ 1 \right]
状态转移方程 d p [ x ] = { d p [ x 1 ] + a [ x ] d p [ x 1 ] 0 a [ x ] d p [ x 1 ] < 0 dp \left[ x \right] = \begin{cases} dp \left[ x-1 \right] + a\left[ x \right] & {dp\left[ x-1 \right] \geq 0}\\ a \left[ x \right] & {dp\left[ x-1 \right] < 0} \end{cases}
计算结果 max 1 i n d p [ i ] \max \limits_{1 \leq i \leq n} dp \left[ i \right]

再来分析一下这个动态规划的合理性。

1、 d p [ x ] dp \left[ x \right] 的设计
最大连续子序列和必然是序列的前 i i 个数中、以某个 a [ i ] a \left[ i \right] 结尾的一个序列。

2、状态转移方程
我们很容易看出,在这种 d p [ x ] dp \left[ x \right] 的设计下,为了使 d p [ x ] dp \left[ x \right] 能够达到最大值, d p [ x ] dp \left[ x \right] 的计算只有两种选择:

  • ①并入前面 d p [ x 1 ] dp \left[ x-1 \right] 对应的序列,继续拼接形成新的连续子序列。
  • ②舍弃前面 d p [ x 1 ] dp \left[ x-1 \right] 对应的序列,自己本身单独形成一个新的连续子序列。

这两种方式我们要找到一个值最大的,也就是 d p [ x ] = max { d p [ x 1 ] + a [ x ] , a [ x ] } dp \left[ x \right] = \max \left\{ dp \left[ x-1 \right]+a \left[ x \right] , a \left[ x \right] \right\} ,其实和上面表格中列出的公式是一个意思,可能显得简化一些。

3、初始状态
最初始的状态很显然就是前1个数中必须包含 a [ 1 ] a \left[ 1 \right] 的最大连续子序列和, d p [ 1 ] = a [ 1 ] dp \left[ 1 \right] = a \left[ 1 \right] .

4、计算结果
最终的结果就是所有 d p [ x 1 ] dp \left[ x-1 \right] 中,值最大而且还靠前的那组解。

对于位置信息,我们也可以另外写上一个start数组,用 start [ x ] \text{start} \left[ x \right] 来标记 d p [ x ] dp \left[ x \right] 对应序列的起始位置。每个案例的输出结果依次为 d p [ x ] dp \left[ x \right] start [ x ] \text{start} \left[ x \right] x x .

对于格式要求,两个案例间一个空行,可以理解为最后一个案例后不加空行。因此,只要在打印时,根据案例计数器作一个判断就可以了。

题解代码

HDU(C++/G++)AC代码如下:

#include <iostream>
#include <algorithm>

using namespace std;

int a[100005];
int dp[100005];
int start[100005];

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	//数据准备
	int T;
	int cnt = 1;
	int n;
	//样例数
	cin >> T;
	while (T--)
	{
		//接收输入
		cin >> n;
		for (int i = 1; i <= n; ++i)
			cin >> a[i];
		//初始化状态
		dp[1] = a[1];
		start[1] = 1;
		//状态转移,开始dp
		//状态转移方程:
		//    dp[x] = dp[x - 1] + a[x](dp[x - 1] >= 0)
		//    dp[x] = a[x](dp[x - 1] < 0)
		for (int i = 2; i <= n; ++i)
		{
			if (dp[i - 1] >= 0)
			{
				dp[i] = dp[i - 1] + a[i];
				start[i] = start[i - 1];
			}
			else
			{
				dp[i] = a[i];
				start[i] = i;
			}
		}
		//搜素结果
		int maxt = 1;
		for (int i = 2; i <= n; ++i)
			if (dp[i] > dp[maxt])
				maxt = i;
		//输出结果
		cout << "Case " << cnt++ << ':' << endl;
		cout << dp[maxt] << ' ' << start[maxt] << ' ' << maxt << endl;
		if (T)
			cout << endl;
	}

	return 0;
}

评价

这道题算是一道动态规划(dp)的入门题,要想熟练使用dp,还是得多刷题长长见识啊。

发布了4 篇原创文章 · 获赞 4 · 访问量 228

猜你喜欢

转载自blog.csdn.net/qq_44220418/article/details/104439532