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

Max Sum of Max-K-sub-sequence

求解一个数字环中的长度不超过k的连续之和,使得和最大,若存在多个,输出起始位置最小的,同时,长度最短的。

一个单调队列的问题,需要对问题有一个很清楚的了解。设nsum[i]表示前i个数字的和,那么很容易,表示出从i+1 开始的到位置j结束的这串数字的和: nsum[j] - nsum[i],由此我们就可以很容易,得到此问题的解,max{ nsum[j]  - nsum[i]},其中 j - i <= k -1 ;问题,到这里还是很难解决。在考虑一下,如果枚举j,我们只需要计算出nsum[j] - nsum[i]的最大值,当j给定的时候,问题就变成找出最小的nsum[i]。此时问题就转化为,max{nsum[j] - min{ nsum[i] } }。那么如何高效的求解区间中的最小的nsum[i]?利用单调队列,维护一个单调递增的队列即可。这样我们就可以很容易的找出序列中的最小元素,那么也就很容易的找出对应的答案。至于环的问题,我们只需要在复制一段即可。

程序如下:

/*
ID: csuchenan
PROG: hdoj 3415
LANG: C++
*/

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std ;

#define MAXN 200005 

struct Node{
	int pos ;
	int val ;
}node[MAXN] ;

int nval[MAXN] ;
int nsum[MAXN] ;

int n ;
int k ;

void init() ;
void work() ;

int main(int argc , char *argv[]){
	
	int m ;
	
	scanf("%d" , &m) ;
	
	while(m--){
		init() ;
		work() ;
		
	}
	
	//system("pause") ;
	return 0 ;
}

void init(){
		
	scanf("%d %d" , &n , &k) ;
	
	int i ;
	
	memset(nsum , 0 , sizeof(nsum)) ;
	memset(nval , 0 , sizeof(nval)) ;
	
	for(i = 1 ; i <= n ; i ++){
		scanf("%d" , &nval[i]) ;
		nsum[i] = nsum[i-1] + nval[i] ;	
	}
	
	for(i = n + 1 ; i <= n + k ; i ++){
		nsum[i] = nsum[i-1] + nval[i-n] ;
	}
	return ; 
} 

void work(){
	
	int front  ;
	int rear   ;
	
	front = 1 ;
	rear  = 1 ;
	
	node[1].pos = 1 ;
	node[1].val = 0 ;
	
	int i ;
	int nmax ;
	int spos ;
	int epos ;
	
	nmax = nsum[1] ;
	spos = 1 ;
	epos = 1 ;
	
	for(i = 2 ; i <= n + k ; i ++){
		//维护一个单调增队列
		while(front <= rear && node[rear].val > nsum[i-1]){
			rear -- ;
		}
		
		rear ++ ;
		
		node[rear].pos = i ;
		node[rear].val = nsum[i-1] ;
		//删除队列中老元素,即那些位置超过i-k的。
		while(node[front].pos <= i - k){
			front ++ ;
		}
		//更新最大值
		if(nmax < nsum[i] - node[front].val){
			nmax = nsum[i] - node[front].val ;
			epos = i ;
			spos = node[front].pos ;
		}	
		
	}
	//此处由于是环,求摸
	if(epos > n)
		epos = epos - n ;
	if(spos > n)
		spos = spos - n ;	
	
	printf("%d %d %d\n" , nmax , spos , epos) ;
	
	return ;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值