杭电1003最大子序列和(DP)

最大子序列和 ##–动态规划

杭电1003;
Description
Given a sequence a[1],a[2],a[3]…a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14.
Input
The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000).
Output
For each test case, you should output two lines. The first line is “Case #:”, # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases.
Sample Input
2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7 -5
Sample Output
Case 1:
14 1 4

Case 2:
7 1 6

大意: 
  给出一个序列,求出序列中连续的数的和的最大值并输出连续数列中首尾的位置,比如6 -1 5 4 -7 中6-1+5+4=14为连续数列中和最大的。
  输入:
    先输入t,表示t组测试数据;
    每组测试数据第一个数为序列的个数;
  输出:
    输出要求的最大和,和求出的序列的首尾位置。

这是一道用动态规划算法得出的习题,用足够时间终于ac啦,下面给出详细思维过程:
一开始总有这样的思维过程,依据动态规划算法的思路,认为要找出最大子序列和,则找出去掉末尾的数的最大子序列即可,然而在寻找去掉末尾数的最大子序列和的时候,要考虑
1:不包含最后一个数的最大连续子序列和
2:包含最后一个数的最大连续子序列和
3:该数字本身
4:......
思考到这里陷入死胡同,感觉思考的方向本身就有问题,出发点一开始就错了,怎么办呢?

暴力计算下是否有规律可循?
是不是真的要检索所有的可能性?
动态规划上是不是不是最大的动态规划,而是检索的动态规划?

于是慢慢发现有如下规律:
如果把所有的子序列拿出来分类,把以相同数为末尾的归为一类,可得到
(假设有a[0],a[1],a[2],…a[n],n+1个数)
a[0];
a[0] a[1]; a[1]
a[0] a[1] a[2]; a[1] a[2]; a[2];
a[0] a[1] a[2] a[3]; a[1] a[2] a[3]; a[2] a[3]; a[3];


规律
下一行只是在上一行的基础上多了一个末尾的数,如果我知道上一行最大的子序列,那么下一行最大的子序列一定在它的正下方(末尾数除外),因为上一行每一个子序列加上同一个数以后它的最大子序列还是它,(尽管最大子序列和有可能减少,因为这只是检索,只要不更maxsum就好),由此,只需要比较加上这个数与这个数的大小,就可以得到总的最大子序列和了
于是状态转移方程为:
sum[i]=max(sum[i-1]+a[i-1],a[i])
以下是ac代码,写的很啰嗦

#include<iostream>//最大子序列和
#include<cmath>
#include<algorithm>
#include<stdio.h>
using namespace std;
int main()
{
	int num, i, a[100000], maxsum, sum, t, B, F, k, tongji, j;
	cin >> t;
	k = 0;
	while (t--)
	{
		cin >> num;
		maxsum = -1001;
		sum = -1000;
		for (i = 0; i < num; i++)
			cin >> a[i];
		tongji = 0;
		for (i = 0; i < num; i++)
		{
			if (a[i] < 0)
				tongji++;
			if (sum + a[i] >= a[i])
				sum = sum + a[i];
			else
			{
				sum = a[i];
			}
			if (sum > maxsum)
			{
				maxsum = sum;
				F = i + 1;
			}
		}
		sum = 0;
		for (i = F - 1; i >= 0; i--)
		{
			sum += a[i];
			if (sum == maxsum)
			{
				B = i + 1;
			}
		}//寻找起始值
		if (tongji == num)//当所有的值为负数时
		{
			for (i = 0; i < num; i++)
			{
				if (a[i] == maxsum)
				{
					maxsum = a[i];
					j = i;
					break;
					//cout << j;
				}

			}
			printf("Case %d:\n", ++k);
			printf("%d %d %d\n", maxsum, B, F);
			if (t)
				printf("\n");//注意输出格式          }
			//cout << "Case " << k << ":" << endl << maxsum << " " << j + 1 << " " << j + 1 << endl << endl;
		}
		else
		{
			printf("Case %d:\n", ++k);
			printf("%d %d %d\n", maxsum, B, F);
			if (t)
				printf("\n");//注意输出格式    
				//cout << "Case " << k << ":" << endl << maxsum << " " << B << " " << F << endl << endl;
		}
	}
	//cin >> i;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/swcainiao/article/details/82353951