hud 3415 Max Sum of Max-K-sub-sequence

题目

http://acm.hdu.edu.cn/showproblem.php?pid=3415

分析

做的时候要注意由于首尾相连,所以我们就扩出一倍的空间来简化这个过程。
最朴素的做法,在处理第i个元素的时候,将以i开始的长度为1...K的所有情况算一遍然后和以前存储的结果比较,保存中间结果,此时时间复杂度为O(N*K),很显然会超时,需要另想办法优化。
考虑到最朴素的做法中可以使用maxn[i]表示以i为结尾,长度不超过k的非空子序列的最大和(即子序列的和用两个前i项和的差表示),那么有maxn[i]=sum[i]-min{sum[i-1]...sum[i-k]}(因为求以i结尾的序列和的时候,前i项和是确定的不变化,所以可以用方法优化方程后半部分),直接做的话时间复杂度O(n*k),看到表达式中后一部分,可以采用单调队列解决,维护一个sum[i]单调递增的单调队列,那么每次求解maxn[i]就可以直接取队列头,复杂度降到O(1),由于长度不大于k,所以每次插入之后还要删掉不满足条件的队头元素。

复杂度

时间复杂度O(N+K);控件复杂度O(N);

设计内容

算法:动态规划,用于存取前i项和
数据结构:单调队列,用于求某个区间的最小值(注意和单调栈的区别)

感想

在求子序列和的最值的时候可以使用的方法:
1.朴素方法
2.用两个前i项和做差得到中间部分的子序列的和。此时,由于第一个值(前i项和)已经确定,所以可以使用单调队列的优化方法,对第二参数进行优化(好方法)
3.当思路正确但是任然WA的时候,可以考虑是不是初始化错误,考虑正负、最大最小、临界情况;(参考 算法解析之感想---动态规划算法的初始化和转移)
4.当出现环的情况,可以通过开辟二倍空间,将环编程一个一维数组

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long sum3415[200010];
int id3415[200010];
int main()
{
	freopen("in.txt","r",stdin);
	long T,N,K;scanf("%ld",&T);
	long result,start=0,end=0;
	while(T--)
	{
		memset(sum3415,0,sizeof(sum3415)),memset(id3415,0,sizeof(id3415));
		scanf("%ld %ld",&N,&K);
		for(int i=1;i<=N;++i)
		{
			scanf("%d",&sum3415[i]);
			sum3415[i+N]=sum3415[i];
		}
		for(int i=2;i<N+K;++i)
			sum3415[i]+=sum3415[i-1];
		int front=0,rear=0;
		result=-100000000;
		for(int i=1;i<N+K;++i)
		{
			while(front<=rear && sum3415[i-1]<=sum3415[id3415[rear]]) --rear;//对第i项计算的时候,求前i-1项的最小值,很重要的理解
			id3415[++rear]=i-1;
			while(front<=rear && i-id3415[front]>K) ++front;
			if(sum3415[i]-sum3415[id3415[front]]>result)
			{
				result=sum3415[i]-sum3415[id3415[front]];
				start=id3415[front]+1;
				end=i;
			}
		}
		printf("%ld %ld %ld\n",result,start,(end>N?end%N:end));
	}
	return 0;
}

参考文献

1. http://blog.sina.com.cn/s/blog_6ffc3bde01015l2m.html(单调队列和单调栈的区别)
2. http://blog.csdn.net/dgq8211/article/details/7443851
3. http://www.cnblogs.com/Lyush/archive/2012/08/18/2645978.html(代码注释比较详细)
4.h ttp://www.cppblog.com/baby-fly/archive/2010/08/04/122213.aspx(用队列实现)

你可能感兴趣的:(数据结构,算法,单调队列)