Max Sum of Max-K-sub-sequence
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3415
题意:一个长度为n包含正负整数的数环,即第1个的左边是第n个。从中选一个不超过k的序列,使得序列和最大,最大值相同选开始点最小的,开始点相同选长度最小的。
题解:不难看出动态方程为dp[i]=max{sum[i]-sum[j]}(i-k<j<i) ,dp[i]表示以i结尾的最大值,sum表示从1到i的序列和,枚举的话要0(n^2),明显要超时,这时可以使用单调队列维护一个非递减队列,因为对于确定的i,要使和最大就得使sum[j]最小,而单调队列的队头就是能选的j中sum[j]最小的。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 100005
#define inf 0xfffff
int summ[MAX<<1];//summ[i]代表1到i的和
int value[MAX<<1];//value[i]代表第i个值
int q[MAX<<1];//模拟队列,存放选取位置的下标
int front,back;//队列头尾
int main()
{
int t,n,m,k,cnt;
scanf("%d",&t);
for(;t--;)
{
summ[0]=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
{
scanf("%d",value+i);
summ[i]=value[i]+summ[i-1];
}
for(int i=n+1;i<=n+k;++i) summ[i]=summ[i-1]+value[i-n];
m=n+k-1;
front=back=1;
int ans=-inf,st,en;
//dp[i]=max{summ[i]-summ[j]}(i-k<j<i) dp[i]为以第i个结尾的最大值
//对于每个i要使最大值最大,就得选summ[j]最小的
for(int i=1;i<=m;++i)
{
for(;front<back&&summ[i-1]<summ[q[back-1]];--back);//维护单调非递减序列
q[back++]=i-1;
for(;front<back&&i-q[front]>k;++front);//将队头超出范围的舍弃
if(summ[i]-summ[q[front]]>ans)
{
ans=summ[i]-summ[q[front]];
st=q[front]+1;
en=i;
}
}
if(en>n) en%=n;
printf("%d %d %d\n",ans,st,en);
}
return 0;
}
来源: http://blog.youkuaiyun.com/acm_ted/article/details/7909178