HDU 3415

题意:给出一个有N个数字(-1000..1000,N<=10^5)的环状序列,让你求一个和最大的连续子序列。这个连续子序列的长度小于等于K。这题是我们比赛题,当时不会写,不会单调队列,现在会了,果断把它给A了,但还是贡献了很多个wa,tle,re,先前wa就一直改,本来对的改的乱七八糟,其实就是数组开小了,蛋疼了一个晚上,后面果断删掉重写,re后就A了,这题其实很好想,可以说一般人都知道怎么做,首先我先讲讲dp中的最大子段和,有几种解法,直接暴力o(n^3),部分和o(n^2),分治o(nlg(n)),dp o(n);如果说有人硬要说这题是dp,我没办法,还说了状态的转移可以证明,我的第一直观这种一段一段的和而且是要求位置的题目,一般都是用部分和求解的,虽然我很菜,也知道dp一般是对全局求最优,求子问题还不如用分治(不过还要找位置就麻烦了),这题就是一个部分和的问题,sum[i]-sum[i-j]=sum(i~j);这东西小学生都知道,不需要我去证明了,问题就是求max(sum[i-j](i-j<=k)), 显然是sum[i]-sum[j]最大,i-k<j<i,就是求min sum[j];这就是一个单调队列的问题了,只要枚举终点的位置就ok了。

单调对列的stl写法很简单,别人写的。看到就贴过来了(插入的下标)

 deque<int> q;
        q.clear();
  while(!q.empty() && sum[j-1]<sum[q.back()])
        q.pop_back();
  while(!q.empty() && q.front()<(j-K))
        q.pop_front();
        q.push_back(j-1);
我的用的是手写版的,还好吧

#include<stdio.h>
struct queuey
{
	int key,flag;
}q[200005];
int r,f;
int insert(int flag,int key)
{
	while(r>f&&key<q[r].key)
		r--;
	q[++r].flag=flag;
	q[r].key=key;
	return 1;
}
void init()
{
	f=r=0;
}
int a[100005],sum[200005];
int main()
{
	int n,m,c,ans,x,y,i;
	scanf("%d",&c);
	while(c--)
	{
		scanf("%d%d",&n,&m);
		sum[0]=0;init();
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			sum[i]=sum[i-1]+a[i];
		}
		for(i=n+1;i<n+m;i++)
			sum[i]=sum[i-1]+a[i-n];
		ans=-0x3fffffff;
		for(i=1;i<n+m;i++)
		{
			while(q[f+1].flag<i-m&&f<r)
					f++;
				insert(i-1,sum[i-1]);				
				if(sum[i]-q[f+1].key>ans)
				{
					ans=sum[i]-q[f+1].key;
					x=q[f+1].flag+1;
				    y=i;
				}
		}
		printf("%d %d %d\n",ans,x,y<=n?y:y%n);
	}
	return 0;
}


你可能感兴趣的:(HDU 3415)