hdu 3415"Max Sum of Max-K-sub-sequence"(单调队列)
题意:
给出一个有 N 个数字([-1000 , 1000],N ≤ 105)的环状序列;
让你求一个和最大的连续子序列,并记录起始点。
要求这个连续子序列的长度小于等于K,加和相同的不同区间,输出起点最小的那组答案。
题解:
因为序列是环状的,所以可以在序列后面复制一段(或者复制前k - 1个数字)。
如果用sum[ i ]来表示复制过后的序列的前 i 个数的和;
那么任意一个子序列[ i..j ]的和就等于s[ j ]-s[ i-1 ]。
对于每一个 j,用s[ j ]减去最小的一个s[ i ](i ≥ j-k+1)就可以得到以 j 为终点长度不大于k的和最大的序列了。
将原问题转化为这样一个问题后,就可以用单调队列解决了。
AC代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<deque> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 #define INF 0x3f3f3f3f 8 #define pii pair<int ,int > 9 const int maxn=2e5+50; 10 11 int n,k; 12 int a[maxn]; 13 int sum[maxn]; 14 deque<int >deq; 15 16 void Solve() 17 { 18 sum[0]=0; 19 for(int i=1;i < 2*n;++i) 20 sum[i]=sum[i-1]+a[i]; 21 while(!deq.empty()) 22 deq.pop_back(); 23 deq.push_back(0); 24 25 int ans=-INF; 26 int ansL,ansR; 27 for(int i=1;i < 2*n;++i) 28 { 29 while(!deq.empty() && deq.front() < i-k) 30 deq.pop_front();///维护[i-k,i-k+1,...,i-1]的最小值 31 int curSum=sum[i]-sum[deq.front()]; 32 if(curSum > ans) 33 { 34 ans=curSum; 35 ansL=deq.front()+1; 36 ansR=i; 37 } 38 while(!deq.empty() && sum[deq.back()] >= sum[i]) 39 deq.pop_back(); 40 deq.push_back(i); 41 } 42 if(ansR > n) 43 ansR -= n; 44 printf("%d %d %d\n",ans,ansL,ansR); 45 } 46 int main() 47 { 48 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 49 int test; 50 while(~scanf("%d",&test)) 51 { 52 while(test--) 53 { 54 scanf("%d%d",&n,&k); 55 for(int i=1;i <= n;++i) 56 { 57 scanf("%d",a+i); 58 a[n+i]=a[i]; 59 } 60 Solve(); 61 } 62 } 63 return 0; 64 }