码迷,mamicode.com
首页 > 其他好文 > 详细

杭电ACM3415——Max Sum of Max-K-sub-sequence

时间:2015-05-05 14:35:54      阅读:173      评论:0      收藏:0      [点我收藏+]

标签:acm   杭电   

一开始,看到这题,以为是最大连续子序列和的问题,写出了代码,提交了,WR,找了一些测试数据,结果发现这个算法并不能将所以的序列的解求出,只是满足一部分序列。

百度了一下,知道了要用单调队列来求解。

单调队列,也就是队列中必然是单调递减的或者递增的。而这题使用的是单调递增的队列。

单调队列使用的是双向队列,队尾队头都可以删除元素,只能从队尾插入元素。

比如求解一个数列{1  ,2  ,5 ,3, 4, 6}的最长的递增序列的长度。

首先,1入队,队列中有 1。 接下来2比1 大,2入队,队列为 1 ,2

接下来5比2大,5入队,队列为1, 2, 5.

接下来3比5小,删除5,3再与新的队尾比较,3比2大,将3入队,队列为1 ,2, 3。

以此类推:最后队列为 1, 2, 3, 4, 6。

而杭电3415这一题,是求一个环状的数列的最大连续序列的和,序列长度不大于K。数列的第一项和最后一项相邻。

很多人一开始的想法就是DP,可是DP并不能解决,这个我前面已经讲到了。

输入序列的长度N,和最大序列的长度为K。

因为数列是环状的,所以将数组扩大一倍,输入时,a[i] = a[i + N](1 <= i <= N);这样就解决了环状的问题。

题目是求最大连续序列的和,所以用一个数组sum来存前i个数的和(1 <= i <= 2 * N),sum[i] = sum[i - 1] + a[i].

这样问题求解的最大连续子序列和(ans)就变成了求解sum数组中在长度不超过K的情况下,ans = sum【j】- sum【i】。sum【i】为sum中相对最小的,sum【j】为相对最大的,j - i  + 1 <= K(因为有长度限制)。这样不断更新ans。这里的队列不是单调递增的,那就等价于 拿每一个sum数组的每一个与它的前面的每一个相减来更新ans,时间复杂度很大,必定超时,而使用单调队列,可以避免一些无谓的步骤。

下面是AC的代码,看注释加上上面的应该就可以理解了.

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;

int a[200005], sum[200005];
int main()
{
	int t, N, K, i;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &N, &K);
		int n = N;                                   //备份N
		sum[0] = 0; 
		for(i = 1; i <= N; i++)                      //输入
		{
			scanf("%d", &a[i]);
			a[N + i] = a[i];
			sum[i] = sum[i - 1] + a[i];              //求前i项的序列和(1 <= i <= N)
		}
		for(i = N + 1; i <= 2 * N; i++)
			sum[i] = sum[i - 1] + a[i];              //求前i项的序列和(N + 1 <= i <= 2 * N)
		deque<int> que;                              //定义双向队列
		int ans = -10000000;
		int start, end;
		N = N + K - 1;                               //序列长度不超过K,只需要前面的N + K - 1项就足以,
		que.clear();
		for(i = 1; i <= N; i++)                      //单调队列保持为递增的序列。
		{
			while(!que.empty() && sum[i - 1] < sum[que.back()])     //插入的数比队尾的小,删除队尾,再比较,直到比队尾大。
				que.pop_back();                                 //删除的元素的价值比插入的元素的价值小。因为要序列和最大
										//下面的ans要不断的更新,队头的元素要相对最小,使解最优。
			while(!que.empty() && i - que.front() > K)              //队列长度大于K,删除队头。
				que.pop_front();                                //保持长度不大于K,不断的更新ans
			que.push_back(i - 1);                                   //加入队尾
			if(sum[i] - sum[que.front()] > ans)                     //判断序列和是否大于ans,不断更新ans
			{
				ans = sum[i] - sum[que.front()];
				start = que.front() + 1;
				end = i;
			}
		}
		if(end > n)                                  //末尾位置超过n,减掉n。
			end -= n;
		printf("%d %d %d\n", ans, start, end);
	}
	return 0;
}

对于单调队列也是初学,如有错误,欢迎指正,互相学习。~~

杭电ACM3415——Max Sum of Max-K-sub-sequence

标签:acm   杭电   

原文地址:http://blog.csdn.net/qq_25425023/article/details/45500457

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!